From 9558a7d06b5e3df10b67c4a97afd7dfc79a573ac Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 19 Jun 2025 11:30:46 +0200 Subject: [PATCH 01/41] :art: format markdowns --- docs/README.md | 16 +- docs/vuegen_basic_case_study_configfile.md | 327 +++++++++++---------- 2 files changed, 172 insertions(+), 171 deletions(-) diff --git a/docs/README.md b/docs/README.md index 8e11f08..2c090f5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,10 +1,10 @@ # Docs creation -In order to build the docs you need to +In order to build the docs you need to - 1. Install sphinx and additional support packages - 2. Build the package reference files - 3. Run sphinx to create a local html version +1. Install sphinx and additional support packages +2. Build the package reference files +3. Run sphinx to create a local html version The documentation is build using readthedocs automatically. @@ -18,12 +18,13 @@ poetry install --with docs ## Build docs using Sphinx command line tools -Command to be run from `path/to/docs`, i.e. from within the `docs` package folder: +Command to be run from `path/to/docs`, i.e. from within the `docs` package folder: Options: - - `--separate` to build separate pages for each (sub-)module -```bash +- `--separate` to build separate pages for each (sub-)module + +```bash # pwd: docs # apidoc sphinx-apidoc --force --implicit-namespaces --module-first -o reference ../src/vuegen @@ -38,4 +39,3 @@ The README is included in the `Overview` section of the docs. We created a [Pyth Relative links are used in the main README, which need to be resolved when building. It's possible to include the a `relative-docs` option if one uses `index.md` ([see docs](https://myst-parser.readthedocs.io/en/latest/faq/index.html#include-a-file-from-outside-the-docs-folder-like-readme-md)). This does not work with `href` links, only native markdown links. - diff --git a/docs/vuegen_basic_case_study_configfile.md b/docs/vuegen_basic_case_study_configfile.md index 2336459..c4faabf 100644 --- a/docs/vuegen_basic_case_study_configfile.md +++ b/docs/vuegen_basic_case_study_configfile.md @@ -1,6 +1,6 @@ # Predefined Directory Case Study - Configuration File -The [configuration file](https://github.com/Multiomics-Analytics-Group/vuegen/blob/main/docs/example_config_files/Basic_example_vuegen_demo_notebook_config.yaml) of the basic case study using a predefined directory is presented below: +The [configuration file](https://github.com/Multiomics-Analytics-Group/vuegen/blob/main/docs/example_config_files/Basic_example_vuegen_demo_notebook_config.yaml) of the basic case study using a predefined directory is presented below: ```yaml report: @@ -9,168 +9,169 @@ report: graphical_abstract: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.svg logo: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.svg sections: -- title: Plots - description: This section contains example plots. - subsections: - - title: Interactive Plots - description: Optional description for section. - components: - - title: Top Species Plot By Biome Plotly - file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/1_top_species_plot_by_biome_plotly.json - description: '' - caption: '' - component_type: plot - plot_type: plotly - - title: Multiline Plot Altair - file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/2_multiline_plot_altair.json - description: '' - caption: '' - component_type: plot - plot_type: altair - - title: Pie Plot Countries Plotly - file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/3_pie_plot_countries_plotly.json - description: '' - caption: '' - component_type: plot - plot_type: plotly - - title: Pie Plots Biomes Plotly - file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/4_pie_plots_biomes_plotly.json - description: '' - caption: '' - component_type: plot - plot_type: plotly - - title: Saline Metagenomics Samples Map Altair - file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/5_saline_metagenomics_samples_map_altair.json - description: '' - caption: '' - component_type: plot - plot_type: altair - - title: Description - file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/description.md - description: '' - caption: '' - component_type: markdown - - title: Static Plots - description: '' - components: - - title: Number Samples Per Study - file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/2_Static_plots/1_number_samples_per_study.png - description: '' - caption: '' - component_type: plot - plot_type: static - - title: Animal Metagenomics Samples Map - file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/2_Static_plots/2_animal_metagenomics_samples_map.png - description: '' - caption: '' - component_type: plot - plot_type: static - - title: Alpha Diversity Host Associated Samples - file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/2_Static_plots/3_alpha_diversity_host_associated_samples.png - description: '' - caption: '' - component_type: plot - plot_type: static - - title: "Graphical overview of VueGen workflow and components" - file_path: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_graph_abstract.png - description: '' - caption: The diagram illustrates the processing pipeline of VueGen, starting - from either a directory or a YAML configuration file. Reports consist of hierarchical - sections and subsections, each containing various components such as plots, - dataframes, Markdown, HTML, and data retrieved via API calls. - component_type: plot - plot_type: static -- title: Dataframes - description: '' - subsections: - - title: All Formats - description: This subsection contains example dataframes. - components: - - title: Phyla Correlation Network Csv - file_path: example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/1_phyla_correlation_network_csv.csv - description: '' - caption: '' - component_type: dataframe - file_format: csv - delimiter: ',' - - title: Abundance Table Example Xls - file_path: example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/2_abundance_table_example_xls.xls - description: '' - caption: '' - component_type: dataframe - file_format: xls - - title: Sample Info Example Txt - file_path: example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/3_sample_info_example_txt.txt - description: '' - caption: '' - component_type: dataframe - file_format: txt - delimiter: \t - - title: Sample Info Example Parquet - file_path: example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/4_sample_info_example_parquet.parquet - description: '' - caption: '' - component_type: dataframe - file_format: parquet -- title: Networks - description: '' - subsections: - - title: Interactive Networks - description: Optional description for subsection - components: - - title: Man Example - file_path: example_data/Basic_example_vuegen_demo_notebook/3_Networks/1_Interactive_networks/1_man_example.graphml - description: '' - caption: '' - component_type: plot - plot_type: interactive_network - - title: Description - file_path: example_data/Basic_example_vuegen_demo_notebook/3_Networks/1_Interactive_networks/description.md - description: '' - caption: '' - component_type: markdown - - title: Static Networks - description: '' - components: - - title: Phyla Correlation Network - file_path: example_data/Basic_example_vuegen_demo_notebook/3_Networks/2_Static_networks/1_phyla_correlation_network.png - description: '' - caption: '' - component_type: plot - plot_type: static -- title: Html - description: '' - subsections: - - title: All Html - description: '' - components: - - title: Plot - file_path: example_data/Basic_example_vuegen_demo_notebook/4_Html/1_All_html/1_plot.html - description: '' - caption: '' - component_type: html - - title: Ckg Network - file_path: example_data/Basic_example_vuegen_demo_notebook/4_Html/1_All_html/2_ckg_network.html - description: '' - caption: '' - component_type: plot - plot_type: interactive_network - - title: Multiqc Report - file_path: example_data/Basic_example_vuegen_demo_notebook/4_Html/1_All_html/3_multiqc_report.html - description: '' - caption: '' - component_type: html -- title: Markdown - description: '' - subsections: - - title: All Markdown - description: '' - components: - - title: Readme - file_path: example_data/Basic_example_vuegen_demo_notebook/5_Markdown/1_All_markdown/README.md - description: '' - caption: '' - component_type: markdown + - title: Plots + description: This section contains example plots. + subsections: + - title: Interactive Plots + description: Optional description for section. + components: + - title: Top Species Plot By Biome Plotly + file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/1_top_species_plot_by_biome_plotly.json + description: "" + caption: "" + component_type: plot + plot_type: plotly + - title: Multiline Plot Altair + file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/2_multiline_plot_altair.json + description: "" + caption: "" + component_type: plot + plot_type: altair + - title: Pie Plot Countries Plotly + file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/3_pie_plot_countries_plotly.json + description: "" + caption: "" + component_type: plot + plot_type: plotly + - title: Pie Plots Biomes Plotly + file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/4_pie_plots_biomes_plotly.json + description: "" + caption: "" + component_type: plot + plot_type: plotly + - title: Saline Metagenomics Samples Map Altair + file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/5_saline_metagenomics_samples_map_altair.json + description: "" + caption: "" + component_type: plot + plot_type: altair + - title: Description + file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/1_Interactive_plots/description.md + description: "" + caption: "" + component_type: markdown + - title: Static Plots + description: "" + components: + - title: Number Samples Per Study + file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/2_Static_plots/1_number_samples_per_study.png + description: "" + caption: "" + component_type: plot + plot_type: static + - title: Animal Metagenomics Samples Map + file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/2_Static_plots/2_animal_metagenomics_samples_map.png + description: "" + caption: "" + component_type: plot + plot_type: static + - title: Alpha Diversity Host Associated Samples + file_path: example_data/Basic_example_vuegen_demo_notebook/1_Plots/2_Static_plots/3_alpha_diversity_host_associated_samples.png + description: "" + caption: "" + component_type: plot + plot_type: static + - title: "Graphical overview of VueGen workflow and components" + file_path: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_graph_abstract.png + description: "" + caption: + The diagram illustrates the processing pipeline of VueGen, starting + from either a directory or a YAML configuration file. Reports consist of hierarchical + sections and subsections, each containing various components such as plots, + dataframes, Markdown, HTML, and data retrieved via API calls. + component_type: plot + plot_type: static + - title: Dataframes + description: "" + subsections: + - title: All Formats + description: This subsection contains example dataframes. + components: + - title: Phyla Correlation Network Csv + file_path: example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/1_phyla_correlation_network_csv.csv + description: "" + caption: "" + component_type: dataframe + file_format: csv + delimiter: "," + - title: Abundance Table Example Xls + file_path: example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/2_abundance_table_example_xls.xls + description: "" + caption: "" + component_type: dataframe + file_format: xls + - title: Sample Info Example Txt + file_path: example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/3_sample_info_example_txt.txt + description: "" + caption: "" + component_type: dataframe + file_format: txt + delimiter: \t + - title: Sample Info Example Parquet + file_path: example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/4_sample_info_example_parquet.parquet + description: "" + caption: "" + component_type: dataframe + file_format: parquet + - title: Networks + description: "" + subsections: + - title: Interactive Networks + description: Optional description for subsection + components: + - title: Man Example + file_path: example_data/Basic_example_vuegen_demo_notebook/3_Networks/1_Interactive_networks/1_man_example.graphml + description: "" + caption: "" + component_type: plot + plot_type: interactive_network + - title: Description + file_path: example_data/Basic_example_vuegen_demo_notebook/3_Networks/1_Interactive_networks/description.md + description: "" + caption: "" + component_type: markdown + - title: Static Networks + description: "" + components: + - title: Phyla Correlation Network + file_path: example_data/Basic_example_vuegen_demo_notebook/3_Networks/2_Static_networks/1_phyla_correlation_network.png + description: "" + caption: "" + component_type: plot + plot_type: static + - title: Html + description: "" + subsections: + - title: All Html + description: "" + components: + - title: Plot + file_path: example_data/Basic_example_vuegen_demo_notebook/4_Html/1_All_html/1_plot.html + description: "" + caption: "" + component_type: html + - title: Ckg Network + file_path: example_data/Basic_example_vuegen_demo_notebook/4_Html/1_All_html/2_ckg_network.html + description: "" + caption: "" + component_type: plot + plot_type: interactive_network + - title: Multiqc Report + file_path: example_data/Basic_example_vuegen_demo_notebook/4_Html/1_All_html/3_multiqc_report.html + description: "" + caption: "" + component_type: html + - title: Markdown + description: "" + subsections: + - title: All Markdown + description: "" + components: + - title: Readme + file_path: example_data/Basic_example_vuegen_demo_notebook/5_Markdown/1_All_markdown/README.md + description: "" + caption: "" + component_type: markdown ``` The directory with he example data is available in the [GitHub repository](https://github.com/Multiomics-Analytics-Group/vuegen/blob/main/docs/example_data/Basic_example_vuegen_demo_notebook). From ca5a970cc0c8e30e194897c4235deee9ba326292 Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 19 Jun 2025 12:32:03 +0200 Subject: [PATCH 02/41] :art: docstrings to 90 characters and remove whitespaces --- src/vuegen/streamlit_reportview.py | 50 ++++++++++++++++++------------ 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/vuegen/streamlit_reportview.py b/src/vuegen/streamlit_reportview.py index 8ed3a8e..640d292 100644 --- a/src/vuegen/streamlit_reportview.py +++ b/src/vuegen/streamlit_reportview.py @@ -260,7 +260,8 @@ def run_report(self, output_dir: str = SECTIONS_DIR) -> None: self.report.logger.debug( f"Running Streamlit report from directory: {output_dir}" ) - # ! using pyinstaller: vuegen main script as executable, not the Python Interpreter + # ! using pyinstaller: vuegen main script as executable, + # ! not the Python Interpreter msg = f"{sys.executable = }" self.report.logger.debug(msg) try: @@ -326,7 +327,8 @@ def _format_text( type : str The type of the text (e.g., 'header', 'paragraph'). level : int, optional - If the text is a header, the level of the header (e.g., 1 for h1, 2 for h2, etc.). + If the text is a header, the level of the header + (e.g., 1 for h1, 2 for h2, etc.). color : str, optional The color of the header text. text_align : str, optional @@ -521,8 +523,9 @@ def _combine_components(self, components: list[dict]) -> tuple[list, list, bool] def _generate_subsection(self, subsection) -> tuple[List[str], List[str]]: """ - Generate code to render components (plots, dataframes, markdown) in the given subsection, - creating imports and content for the subsection based on the component type. + Generate code to render components (plots, dataframes, markdown) in the given + subsection, creating imports and content for the subsection based on the + component type. Parameters ---------- @@ -564,7 +567,8 @@ def _generate_subsection(self, subsection) -> tuple[List[str], List[str]]: def _generate_plot_content(self, plot) -> List[str]: """ - Generate content for a plot component based on the plot type (static or interactive). + Generate content for a plot component based on the plot type + (static or interactive). Parameters ---------- @@ -938,8 +942,8 @@ def _generate_html_content(self, html) -> List[str]: def _generate_apicall_content(self, apicall) -> List[str]: """ - Generate content for an API component. This method handles the API call and formats - the response for display in the Streamlit app. + Generate content for an API component. This method handles the API call and + formats the response for display in the Streamlit app. Parameters ---------- @@ -983,23 +987,26 @@ def _generate_apicall_content(self, apicall) -> List[str]: def _generate_chatbot_content(self, chatbot) -> List[str]: """ - Generate content to render a ChatBot component, supporting standard and Ollama-style streaming APIs. + Generate content to render a ChatBot component, supporting standard and + Ollama-style streaming APIs. - This method builds and returns a list of strings, which are later executed to create the chatbot - interface in a Streamlit app. It includes user input handling, API interaction logic, response parsing, + This method builds and returns a list of strings, which are later executed to + create the chatbot interface in a Streamlit app. It includes user input handling, + API interaction logic, response parsing, and conditional rendering of text, source links, and HTML subgraphs. The function distinguishes between two chatbot modes: - - **Ollama-style streaming API**: Identified by the presence of `chatbot.model`. Uses streaming - JSON chunks from the server to simulate a real-time response. - - **Standard API**: Assumes a simple POST request with a prompt and a full JSON response with text, + - **Ollama-style streaming API**: Identified by the presence of `chatbot.model`. + Uses streaming JSON chunks from the server to simulate a real-time response. + - **Standard API**: Assumes a simple POST request with a prompt and a full JSON + response with text, and other fields like links, HTML graphs, etc. Parameters ---------- chatbot : ChatBot - The ChatBot component to generate content for, containing configuration such as title, model, - API endpoint, headers, and caption. + The ChatBot component to generate content for, containing configuration such + as title, model, API endpoint, headers, and caption. Returns ------- @@ -1074,7 +1081,8 @@ def parse_api_response(response): return {{"role": "assistant", "content": output}} output += body.get("message", {{}}).get("content", "") except Exception as e: - return {{"role": "assistant", "content": f"Error while processing API response: {{str(e)}}"}} + return {{"role": "assistant", "content": + f"Error while processing API response: {{str(e)}}"}} # Simulated typing effect for responses def response_generator(msg_content): @@ -1088,12 +1096,13 @@ def response_generator(msg_content): {handle_prompt_block} # Retrieve question and generate answer - combined = "\\n".join(msg["content"] for msg in st.session_state.messages if msg["role"] == "user") + combined = "\\n".join(msg["content"] for msg in st.session_state.messages + if msg["role"] == "user") messages = [{{"role": "user", "content": combined}}] - with st.spinner('Generating answer...'): + with st.spinner('Generating answer...'): response = generate_query(messages) parsed_response = parse_api_response(response) - + # Add the assistant's response to the session state and display it st.session_state.messages.append(parsed_response) with st.chat_message("assistant"): @@ -1168,7 +1177,8 @@ def _generate_component_imports(self, component: r.Component) -> List[str]: Parameters ---------- component : r.Component - The component for which to generate the required imports. The component can be of type: + The component for which to generate the required imports. + The component can be of type: - PLOT - DATAFRAME From 04e4d5437568617ed8803aed4ec63a7fbba753ad Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 19 Jun 2025 12:32:26 +0200 Subject: [PATCH 03/41] :wrench: set line length to 90 characters --- setup.cfg | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..12062ee --- /dev/null +++ b/setup.cfg @@ -0,0 +1,4 @@ +[flake8] +exclude = docs +max-line-length = 90 +aggressive = 2 From e4c588163ebef06a2d878c5964259502a7423817 Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 19 Jun 2025 12:35:23 +0200 Subject: [PATCH 04/41] :art: split comments, add comments, remove more whitespace, add more docstrings --- src/vuegen/streamlit_reportview.py | 39 +++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/vuegen/streamlit_reportview.py b/src/vuegen/streamlit_reportview.py index 640d292..dc95db9 100644 --- a/src/vuegen/streamlit_reportview.py +++ b/src/vuegen/streamlit_reportview.py @@ -1,3 +1,7 @@ +""" +StreamlitReportView class for generating Streamlit reports based on a configuration file. +""" + import os import subprocess import sys @@ -14,6 +18,7 @@ def write_python_file(fpath: str, imports: list[str], contents: list[str]) -> None: + """Write a Python file with the given imports and contents.""" with open(fpath, "w", encoding="utf8") as f: # Write imports at the top of the file f.write("\n".join(imports) + "\n\n") @@ -75,12 +80,14 @@ def __init__( def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: """ - Generates the Streamlit report and creates Python files for each section and its subsections and plots. + Generates the Streamlit report and creates Python files for each section + and its subsections and plots. Parameters ---------- output_dir : str, optional - The folder where the generated report files will be saved (default is SECTIONS_DIR). + The folder where the generated report files will be saved + (default is SECTIONS_DIR). """ self.report.logger.debug( f"Generating '{self.report_type}' report in directory: '{output_dir}'" @@ -167,14 +174,17 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: self.report.logger.debug( f"Section directory already existed: {section_dir_path}" ) - # add an overview page to section of components exist + # add an overview page to section for it's section components + # they will be written when the components are parsed + # using `_generate_sections` if section.components: subsection_file_path = ( Path(section_name_var) / f"0_overview_{make_valid_identifier(section.title).lower()}.py" ).as_posix() # Make sure it's Posix Paths section.file_path = subsection_file_path - # Create a Page object for each subsection and add it to the home page content + # Create a Page object for each subsection and + # add it to the home page content report_manag_content.append( f"{section_name_var}_overview = st.Page('{subsection_file_path}', title='Overview {section.title}')" ) @@ -194,7 +204,8 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: Path(section_name_var) / f"{subsection_name_var}.py" ).as_posix() # Make sure it's Posix Paths subsection.file_path = subsection_file_path - # Create a Page object for each subsection and add it to the home page content + # Create a Page object for each subsection and + # add it to the home page content report_manag_content.append( f"{subsection_name_var} = st.Page('{subsection_file_path}', title='{subsection.title}')" ) @@ -411,7 +422,8 @@ def _generate_home_section( # Add the home page to the report manager content report_manag_content.append( - "homepage = st.Page('Home/Homepage.py', title='Homepage')" # ! here Posix Path is hardcoded + # ! here Posix Path is hardcoded + "homepage = st.Page('Home/Homepage.py', title='Homepage')" ) report_manag_content.append("sections_pages['Home'] = [homepage]\n") self.report.logger.info("Home page added to the report manager content.") @@ -421,7 +433,8 @@ def _generate_home_section( def _generate_sections(self, output_dir: str) -> None: """ - Generates Python files for each section in the report, including subsections and its components (plots, dataframes, markdown). + Generates Python files for each section in the report, including subsections + and its components (plots, dataframes, markdown). Parameters ---------- @@ -458,7 +471,9 @@ def _generate_sections(self, output_dir: str) -> None: continue # Iterate through subsections and integrate them into the section file - # subsection should have the subsection_file_path as file_path? + # ! subsection should have the subsection_file_path as file_path, + # ! which is set when parsing the config in the main generate_sections + # ! method for subsection in section.subsections: self.report.logger.debug( f"Processing subsection '{subsection.id}': '{subsection.title} -" @@ -603,7 +618,8 @@ def _generate_plot_content(self, plot) -> List[str]: # If network_data is a tuple, separate the network and html file path networkx_graph, html_plot_file = networkx_graph else: - # Otherwise, create and save a new pyvis network from the netowrkx graph + # Otherwise, + # create and save a new pyvis network from the netowrkx graph html_plot_file = ( Path(self.static_dir) / f"{plot.title.replace(' ', '_')}.html" ).resolve() @@ -737,7 +753,8 @@ def _generate_dataframe_content(self, dataframe) -> List[str]: f"Unsupported file extension: {file_extension}. Supported extensions are: {', '.join(fmt.value for fmt in r.DataFrameFormat)}." ) # return [] # Skip execution if unsupported file extension - # Should it not return here? Can we even call the method with an unsupported file extension? + # Should it not return here? + # Can we even call the method with an unsupported file extension? # Build the file path (URL or local file) if is_url(dataframe.file_path): @@ -1067,7 +1084,7 @@ def generate_query(messages): json={{"model": "{chatbot.model}", "messages": messages, "stream": True}}, ) response.raise_for_status() - return response + return response # Parse streaming response from Ollama def parse_api_response(response): From 5a909bbddd340f14f78b07d52457c5c56ae94114 Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 19 Jun 2025 12:37:19 +0200 Subject: [PATCH 05/41] :art: write strings on two line and let Python concatenate theses --- src/vuegen/streamlit_reportview.py | 61 ++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/vuegen/streamlit_reportview.py b/src/vuegen/streamlit_reportview.py index dc95db9..0b122fa 100644 --- a/src/vuegen/streamlit_reportview.py +++ b/src/vuegen/streamlit_reportview.py @@ -106,7 +106,8 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: ) else: self.report.logger.info( - f"Output directory for static content already existed: '{self.static_dir}'" + "Output directory for static content already existed: " + f"{self.static_dir}" ) try: @@ -186,7 +187,8 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: # Create a Page object for each subsection and # add it to the home page content report_manag_content.append( - f"{section_name_var}_overview = st.Page('{subsection_file_path}', title='Overview {section.title}')" + f"{section_name_var}_overview = st.Page('{subsection_file_path}'" + f", title='Overview {section.title}')" ) subsection_page_vars.append(f"{section_name_var}_overview") @@ -195,10 +197,12 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: subsection_name_var = make_valid_identifier(subsection.title) if not subsection_name_var.isidentifier(): self.report.logger.warning( - f"Subsection name '{subsection_name_var}' is not a valid identifier." + f"Subsection name '{subsection_name_var}' " + " is not a valid identifier." ) raise ValueError( - f"Subsection name is not a valid Python identifier: {subsection_name_var}" + "Subsection name is not a valid Python identifier: " + f"{subsection_name_var}" ) subsection_file_path = ( Path(section_name_var) / f"{subsection_name_var}.py" @@ -207,13 +211,15 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: # Create a Page object for each subsection and # add it to the home page content report_manag_content.append( - f"{subsection_name_var} = st.Page('{subsection_file_path}', title='{subsection.title}')" + f"{subsection_name_var} = st.Page('{subsection_file_path}', " + f"title='{subsection.title}')" ) subsection_page_vars.append(subsection_name_var) # Add all subsection Page objects to the corresponding section report_manag_content.append( - f"sections_pages['{section.title}'] = [{', '.join(subsection_page_vars)}]\n" + f"sections_pages['{section.title}'] = " + f"[{', '.join(subsection_page_vars)}]\n" ) # Add navigation object to the home page content @@ -305,7 +311,8 @@ def run_report(self, output_dir: str = SECTIONS_DIR) -> None: else: # 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}" + "All the scripts to build the Streamlit app are available at " + f"{output_dir}" ) self.report.logger.info( "To run the Streamlit app, use the following command:" @@ -314,8 +321,9 @@ def run_report(self, output_dir: str = SECTIONS_DIR) -> None: f"streamlit run {Path(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" + "\nAll the scripts to build the Streamlit app are available at: " + f"{output_dir}\n\n" + "To run the Streamlit app, use the following command:\n\n" f"\tstreamlit run {Path(output_dir) / self.REPORT_MANAG_SCRIPT}" ) print(msg) @@ -403,7 +411,8 @@ def _generate_home_section( ) if self.report.graphical_abstract: home_content.append( - f"\nst.image('{self.report.graphical_abstract}', use_column_width=True)" + f"\nst.image('{self.report.graphical_abstract}', " + "use_column_width=True)" ) # add components content to page (if any) @@ -446,7 +455,8 @@ def _generate_sections(self, output_dir: str) -> None: try: for section in self.report.sections[1:]: self.report.logger.debug( - f"Processing section '{section.id}': '{section.title}' - {len(section.subsections)} subsection(s)" + f"Processing section '{section.id}': '{section.title}' - " + f"{len(section.subsections)} subsection(s)" ) if section.components: @@ -500,8 +510,10 @@ def _generate_sections(self, output_dir: str) -> None: ) except Exception as subsection_error: self.report.logger.error( - f"Error processing subsection '{subsection.id}' '{subsection.title}' " - f"in section '{section.id}' '{section.title}': {str(subsection_error)}" + f"Error processing subsection '{subsection.id}'" + f" '{subsection.title}' " + f"in section '{section.id}' '{section.title}':" + f" {str(subsection_error)}" ) raise @@ -606,7 +618,8 @@ def _generate_plot_content(self, plot) -> List[str]: if plot.plot_type == r.PlotType.STATIC: plot_rel_path = get_relative_file_path(plot.file_path) plot_content.append( - f"\nst.image('{plot_rel_path.as_posix()}', caption='{plot.caption}', use_column_width=True)\n" + f"\nst.image('{plot_rel_path.as_posix()}', " + f" caption='{plot.caption}', use_column_width=True)\n" ) elif plot.plot_type == r.PlotType.PLOTLY: plot_content.append(self._generate_plot_code(plot)) @@ -659,7 +672,8 @@ def _generate_plot_content(self, plot) -> List[str]: self.report.logger.warning(f"Unsupported plot type: {plot.plot_type}") except Exception as e: self.report.logger.error( - f"Error generating content for '{plot.plot_type}' plot '{plot.id}' '{plot.title}': {str(e)}" + f"Error generating content for '{plot.plot_type}' plot '{plot.id}' " + f"'{plot.title}': {str(e)}" ) raise @@ -750,7 +764,10 @@ def _generate_dataframe_content(self, dataframe) -> List[str]: file_extension == fmt.value_with_dot for fmt in r.DataFrameFormat ): self.report.logger.error( - f"Unsupported file extension: {file_extension}. Supported extensions are: {', '.join(fmt.value for fmt in r.DataFrameFormat)}." + f"Unsupported file extension: {file_extension}. " + "Supported extensions are: {}.".format( + ", ".join(fmt.value for fmt in r.DataFrameFormat) + ) ) # return [] # Skip execution if unsupported file extension # Should it not return here? @@ -787,7 +804,8 @@ def _generate_dataframe_content(self, dataframe) -> List[str]: r.DataFrameFormat.XLSX.value_with_dot, ]: dataframe_content.append( - f"""df = pd.{read_function.__name__}('{dataframe.file_path}', sheet_name=selected_sheet)\n""" + f"df = pd.{read_function.__name__}('{dataframe.file_path}'," + " sheet_name=selected_sheet)\n" ) else: dataframe_content.append( @@ -819,7 +837,8 @@ def _generate_dataframe_content(self, dataframe) -> List[str]: ) except Exception as e: self.report.logger.error( - f"Error generating content for DataFrame: {dataframe.title}. Error: {str(e)}" + f"Error generating content for DataFrame: {dataframe.title}. " + f"Error: {str(e)}" ) raise @@ -880,7 +899,8 @@ def _generate_markdown_content(self, markdown) -> List[str]: ) except Exception as e: self.report.logger.error( - f"Error generating content for Markdown: {markdown.title}. Error: {str(e)}" + f"Error generating content for Markdown: {markdown.title}. " + f"Error: {str(e)}" ) raise @@ -998,7 +1018,8 @@ def _generate_apicall_content(self, apicall) -> List[str]: ) self.report.logger.info( - f"Successfully generated content for APICall '{apicall.title}' using method '{apicall.method}'" + f"Successfully generated content for APICall '{apicall.title}' " + f"using method '{apicall.method}'" ) return apicall_content From c2a00169e8b43bcdfbb4a3c38018d67bb8e4292c Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 19 Jun 2025 12:51:04 +0200 Subject: [PATCH 06/41] :art: snake_case --- src/vuegen/streamlit_reportview.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vuegen/streamlit_reportview.py b/src/vuegen/streamlit_reportview.py index 0b122fa..4998f47 100644 --- a/src/vuegen/streamlit_reportview.py +++ b/src/vuegen/streamlit_reportview.py @@ -60,10 +60,10 @@ def __init__( """ super().__init__(report=report, report_type=report_type) self.streamlit_autorun = streamlit_autorun - self.BUNDLED_EXECUTION = False + self.bundled_execution = False if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): self.report.logger.info("running in a PyInstaller bundle") - self.BUNDLED_EXECUTION = True + self.bundled_execution = True else: self.report.logger.info("running in a normal Python process") @@ -287,7 +287,7 @@ def run_report(self, output_dir: str = SECTIONS_DIR) -> None: self.report.logger.debug( f"Running Streamlit report from file: {target_file}" ) - if self.BUNDLED_EXECUTION: + if self.bundled_execution: args = [ "streamlit", "run", From 4cc06c0a1cd73f9c75d68c165e78497e3d355d17 Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 19 Jun 2025 12:53:06 +0200 Subject: [PATCH 07/41] :art: raise ValueError if unknown type is provided alternatively we could use a meaningful default --- src/vuegen/streamlit_reportview.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vuegen/streamlit_reportview.py b/src/vuegen/streamlit_reportview.py index 4998f47..5a99c65 100644 --- a/src/vuegen/streamlit_reportview.py +++ b/src/vuegen/streamlit_reportview.py @@ -358,12 +358,16 @@ def _format_text( str A formatted markdown string for the specified text. """ + tag = "" if type == "header": tag = f"h{level}" elif type == "paragraph" or type == "caption": tag = "p" - - return f"""st.markdown('''<{tag} style='text-align: {text_align}; color: {color};'>{text}''', unsafe_allow_html=True)""" + else: + raise ValueError( + f"Unsupported text type: {type}. Supported types are 'header', " + "'paragraph', and 'caption'." + ) def _generate_home_section( self, From 57948ec95277fa5b83910fc81742eb893c2a85ec Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 19 Jun 2025 12:53:26 +0200 Subject: [PATCH 08/41] :art: specify encoding explicitly --- src/vuegen/streamlit_reportview.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vuegen/streamlit_reportview.py b/src/vuegen/streamlit_reportview.py index 5a99c65..f39f518 100644 --- a/src/vuegen/streamlit_reportview.py +++ b/src/vuegen/streamlit_reportview.py @@ -429,7 +429,7 @@ def _generate_home_section( # Write the home page content to a Python file home_page_path = Path(home_dir_path) / "Homepage.py" - with open(home_page_path, "w") as home_page: + with open(home_page_path, "w", encoding="utf-8") as home_page: home_page.write("\n".join(home_content)) self.report.logger.info(f"Home page content written to '{home_page_path}'.") From 37cc80fcda00178d80493520ee819735d661aafb Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 19 Jun 2025 12:54:13 +0200 Subject: [PATCH 09/41] :art: use textwrap to keep indentation --- src/vuegen/streamlit_reportview.py | 158 +++++++++++++++++++---------- 1 file changed, 103 insertions(+), 55 deletions(-) diff --git a/src/vuegen/streamlit_reportview.py b/src/vuegen/streamlit_reportview.py index f39f518..9cb7263 100644 --- a/src/vuegen/streamlit_reportview.py +++ b/src/vuegen/streamlit_reportview.py @@ -119,8 +119,8 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: """\ import os import time - - import psutil + + import psutil import streamlit as st """ ) @@ -129,7 +129,10 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: report_manag_content.append( textwrap.dedent( f"""\ - st.set_page_config(layout="wide", page_title="{self.report.title}", page_icon="{self.report.logo}") + st.set_page_config(layout="wide", + page_title="{self.report.title}", + page_icon="{self.report.logo}" + ) st.logo("{self.report.logo}") """ ) @@ -138,7 +141,8 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: report_manag_content.append( textwrap.dedent( f"""\ - st.set_page_config(layout="wide", page_title="{self.report.title}") + st.set_page_config(layout="wide", + page_title="{self.report.title}") """ ) ) @@ -227,9 +231,12 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: textwrap.dedent( """\ report_nav = st.navigation(sections_pages) - - # Following https://discuss.streamlit.io/t/close-streamlit-app-with-button-click/35132/5 - exit_app = st.sidebar.button("Shut Down App", icon=":material/power_off:", use_container_width=True) + + # Following https://discuss.streamlit.io/t/\ +close-streamlit-app-with-button-click/35132/5 + exit_app = st.sidebar.button("Shut Down App", + icon=":material/power_off:", + use_container_width=True) if exit_app: st.toast("Shutting down the app...") time.sleep(1) @@ -238,7 +245,7 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None: p = psutil.Process(pid) p.terminate() - + report_nav.run() """ ) @@ -367,7 +374,17 @@ def _format_text( raise ValueError( f"Unsupported text type: {type}. Supported types are 'header', " "'paragraph', and 'caption'." - ) + + return textwrap.dedent( + f""" + st.markdown( + ( + "<{tag} style='text-align: {text_align}; " + "color: {color};'>{text}" + ), + unsafe_allow_html=True) + """ + ) def _generate_home_section( self, @@ -665,9 +682,16 @@ def _generate_plot_content(self, plot) -> List[str]: # Append the code for additional information (nodes and edges count) plot_content.append( - f""" -st.markdown(f"

Number of nodes: {num_nodes}

", unsafe_allow_html=True) -st.markdown(f"

Number of relationships: {num_edges}

", unsafe_allow_html=True)\n""" + textwrap.dedent( + f""" + st.markdown(("

" + f"Number of nodes: {num_nodes}

"), + unsafe_allow_html=True) + st.markdown(("

" + f" Number of relationships: {num_edges}

"), + unsafe_allow_html=True) + """ + ) ) # Add the specific code for visualization @@ -703,37 +727,51 @@ def _generate_plot_code(self, plot) -> str: """ # If the file path is a URL, generate code to fetch content via requests if is_url(plot.file_path): - plot_code = f""" -response = requests.get('{plot.file_path}') -response.raise_for_status() -plot_json = json.loads(response.text)\n""" + plot_code = textwrap.dedent( + f""" + response = requests.get('{plot.file_path}') + response.raise_for_status() + plot_json = json.loads(response.text)\n""" + ) else: # If it's a local file plot_rel_path = get_relative_file_path(plot.file_path) - plot_code = f""" -with open('{plot_rel_path.as_posix()}', 'r') as plot_file: - plot_json = json.load(plot_file)\n""" + plot_code = textwrap.dedent( + f""" + with open('{plot_rel_path.as_posix()}', 'r') as plot_file: + plot_json = json.load(plot_file)\n""" + ) # Add specific code for each visualization tool if plot.plot_type == r.PlotType.PLOTLY: - plot_code += """ -# Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - -# Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] -st.plotly_chart(plot_json, use_container_width=True)\n""" + plot_code += textwrap.dedent( + """ + # Keep only 'data' and 'layout' sections + plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout']} + + # Remove 'frame' section in 'data' + plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', [])] + st.plotly_chart(plot_json, use_container_width=True)\n""" + ) elif plot.plot_type == r.PlotType.ALTAIR: - plot_code += """ -altair_plot = alt.Chart.from_dict(plot_json) -st.vega_lite_chart(json.loads(altair_plot.to_json()), use_container_width=True)\n""" + plot_code += textwrap.dedent( + """ + altair_plot = alt.Chart.from_dict(plot_json) + st.vega_lite_chart(json.loads(altair_plot.to_json()), + use_container_width=True)\n""" + ) elif plot.plot_type == r.PlotType.INTERACTIVE_NETWORK: - plot_code = """# Streamlit checkbox for controlling the layout -control_layout = st.checkbox('Add panel to control layout', value=True) -net_html_height = 1200 if control_layout else 630 -# Load HTML into HTML component for display on Streamlit -st.components.v1.html(html_content, height=net_html_height)\n""" + plot_code = textwrap.dedent( + """\ + # Streamlit checkbox for controlling the layout + control_layout = st.checkbox('Add panel to control layout', value=True) + net_html_height = 1200 if control_layout else 630 + # Load HTML into HTML component for display on Streamlit + st.components.v1.html(html_content, height=net_html_height)\n""" + ) return plot_code def _generate_dataframe_content(self, dataframe) -> List[str]: @@ -796,7 +834,8 @@ def _generate_dataframe_content(self, dataframe) -> List[str]: textwrap.dedent( f"""\ sheet_names = table_utils.get_sheet_names("{dataframe.file_path}") - selected_sheet = st.selectbox("Select a sheet to display", options=sheet_names) + selected_sheet = st.selectbox("Select a sheet to display", + options=sheet_names) """ ) ) @@ -818,26 +857,35 @@ def _generate_dataframe_content(self, dataframe) -> List[str]: # ! Alternative to select box: iterate over sheets in DataFrame # Displays a DataFrame using AgGrid with configurable options. dataframe_content.append( - """ -# Displays a DataFrame using AgGrid with configurable options. -grid_builder = GridOptionsBuilder.from_dataframe(df) -grid_builder.configure_default_column(editable=True, groupable=True, filter=True) -grid_builder.configure_side_bar(filters_panel=True, columns_panel=True) -grid_builder.configure_selection(selection_mode="multiple") -grid_builder.configure_pagination(enabled=True, paginationAutoPageSize=False, paginationPageSize=20) -grid_options = grid_builder.build() - -AgGrid(df, gridOptions=grid_options, enable_enterprise_modules=True) - -# Button to download the df -df_csv = df.to_csv(sep=',', header=True, index=False).encode('utf-8') -st.download_button( - label="Download dataframe as CSV", - data=df_csv, - file_name=f"dataframe_{df_index}.csv", - mime='text/csv', - key=f"download_button_{df_index}") -df_index += 1""" + textwrap.dedent( + """ + # Displays a DataFrame using AgGrid with configurable options. + grid_builder = GridOptionsBuilder.from_dataframe(df) + grid_builder.configure_default_column(editable=True, + groupable=True, + filter=True, + ) + grid_builder.configure_side_bar(filters_panel=True, + columns_panel=True) + grid_builder.configure_selection(selection_mode="multiple") + grid_builder.configure_pagination(enabled=True, + paginationAutoPageSize=False, + paginationPageSize=20, + ) + grid_options = grid_builder.build() + + AgGrid(df, gridOptions=grid_options, enable_enterprise_modules=True) + + # Button to download the df + df_csv = df.to_csv(sep=',', header=True, index=False).encode('utf-8') + st.download_button( + label="Download dataframe as CSV", + data=df_csv, + file_name=f"dataframe_{df_index}.csv", + mime='text/csv', + key=f"download_button_{df_index}") + df_index += 1""" + ) ) except Exception as e: self.report.logger.error( From a0c4ca2cd71e5483c9b09ec0bd8f31eaa51069bc Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 19 Jun 2025 12:57:36 +0200 Subject: [PATCH 10/41] :bug: with textwrap the text cannot contain new line statements --- src/vuegen/streamlit_reportview.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vuegen/streamlit_reportview.py b/src/vuegen/streamlit_reportview.py index 9cb7263..46c3bb3 100644 --- a/src/vuegen/streamlit_reportview.py +++ b/src/vuegen/streamlit_reportview.py @@ -365,7 +365,6 @@ def _format_text( str A formatted markdown string for the specified text. """ - tag = "" if type == "header": tag = f"h{level}" elif type == "paragraph" or type == "caption": @@ -374,6 +373,9 @@ def _format_text( raise ValueError( f"Unsupported text type: {type}. Supported types are 'header', " "'paragraph', and 'caption'." + ) + + text = text.strip() # get rid of new lines return textwrap.dedent( f""" From 05b0d98a3e5fa32c8c50f85f035891e4594a3e4b Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Fri, 20 Jun 2025 09:56:27 +0200 Subject: [PATCH 11/41] :art: adjust comments and docstrings to line-length --- src/vuegen/report.py | 55 +++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/vuegen/report.py b/src/vuegen/report.py index 55aab67..1d24162 100644 --- a/src/vuegen/report.py +++ b/src/vuegen/report.py @@ -87,15 +87,17 @@ def value_with_dot(self): @dataclass class Component: """ - Base class for different components in a report subsection. It encapsulates elements like - plots, dataframes, markdown, or apicalls, providing a consistent structure for report generation. + Base class for different components in a report subsection. It encapsulates elements + like plots, dataframes, markdown, or apicalls, + providing a consistent structure for report generation. Attributes ---------- _id_counter : ClassVar[int] Class-level counter for unique IDs. id : int - Unique identifier for the component, assigned automatically when an object is created. + Unique identifier for the component, assigned automatically + when an object is created. title : str Title of the component. component_type : ComponentType @@ -103,7 +105,8 @@ class Component: logger : logging.Logger Logger object for tracking warnings, errors, and info messages. file_path : Optional[str] - Path to the file associated with the component (e.g., plot JSON file, image file, csv file, etc.). + Path to the file associated with the component + (e.g., plot JSON file, image file, csv file, etc.). caption : Optional[str] Caption providing additional context about the component (default: None). """ @@ -134,7 +137,8 @@ class Plot(Component): plot_type : PlotType The type of the plot (INTERACTIVE or STATIC). csv_network_format : CSVNetworkFormat, optional - The format of the CSV file for network plots (EDGELIST or ADJLIST) (default is None). + The format of the CSV file for network plots (EDGELIST or ADJLIST) + (default is None). """ def __init__( @@ -265,7 +269,8 @@ def read_network(self) -> nx.Graph: f"Unsupported format for CSV/TXT file: {self.csv_network_format}." ) - # Handle other formats using the mapping and return the NetworkX graph object from the specified network file + # Handle other formats using the mapping and return the NetworkX graph object + # from the specified network file G = file_extension_map[file_extension](file_stream) G = self._add_size_attribute(G) self.logger.info(f"Successfully read network from file: {self.file_path}.") @@ -393,7 +398,8 @@ def create_and_save_pyvis_network(self, G: nx.Graph, output_file: str) -> Networ def _add_size_attribute(self, G: nx.Graph) -> nx.Graph: """ - Adds a 'size' attribute to the nodes of a NetworkX graph based on their degree centrality. + Adds a 'size' attribute to the nodes of a NetworkX graph + based on their degree centrality. Parameters ---------- @@ -445,9 +451,11 @@ class DataFrame(Component): Attributes ---------- file_format : DataFrameFormat - The format of the file from which the DataFrame is loaded (e.g., CSV, TXT, PARQUET). + The format of the file from which the DataFrame is loaded + (e.g., CSV, TXT, PARQUET). delimiter : Optional[str] - The delimiter to use if the file is a delimited text format (e.g., ';', '\t', etc). + The delimiter to use if the file is a delimited text format + (e.g., ';', '\t', etc). """ def __init__( @@ -530,7 +538,8 @@ class APICall(Component): api_url : str The URL of the API to interact with. method : str - HTTP method to use for the request ("GET", "POST", or "PUT"). The deafult is "GET". + HTTP method to use for the request ("GET", "POST", or "PUT"). + The deafult is "GET". headers : Optional[dict] Headers to include in the API request (default is None). params : Optional[dict] @@ -617,12 +626,14 @@ def make_api_request( class ChatBot(Component): """ A component for creating a ChatBot that interacts with an API. - This component uses an APICall instance to send requests to the chatbot API and receive responses. + This component uses an APICall instance to send requests + to the chatbot API and receive responses. Attributes ---------- api_call : APICall - An instance of the APICall class used to interact with the API for fetching chatbot responses. + An instance of the APICall class used to interact + with the API for fetching chatbot responses. model : Optional[str] The language model to use for the chatbot (default is None). headers : Optional[dict] @@ -670,7 +681,8 @@ class Subsection: _id_counter : ClassVar[int] Class-level counter for unique IDs. id : int - Unique identifier for the subsection, assigned automatically when an object is created. + Unique identifier for the subsection, assigned automatically + when an object is created. title : str Title of the subsection. components : List[Component] @@ -709,7 +721,8 @@ class Section: _id_counter : ClassVar[int] Class-level counter for unique IDs. id : int - Unique identifier for the section, assigned automatically when an object is created. + Unique identifier for the section, assigned automatically + when an object is created. title : str Title of the section. subsections : List[Subsection] @@ -798,7 +811,8 @@ def generate_report(self, output_dir: str = "sections") -> None: Parameters ---------- output_dir : str, optional - The folder where the generated report files will be saved (default is 'sections'). + The folder where the generated report files will be saved + (default is 'sections'). """ pass @@ -822,7 +836,8 @@ def _generate_component_imports(self) -> str: Parameters ---------- component : r.Component - The component for which to generate the required imports. The component can be of type: + The component for which to generate the required imports. + The component can be of type: - PLOT - DATAFRAME - MARKDOWN @@ -852,7 +867,8 @@ def _format_text(self, text: str, type: str, level: int, color: str) -> str: type : str The type of the text (e.g., 'header', 'paragraph'). level : int, optional - If the text is a header, the level of the header (e.g., 1 for h1, 2 for h2, etc.). + If the text is a header, the level of the header + (e.g., 1 for h1, 2 for h2, etc.). color : str, optional The color of the header text. @@ -884,8 +900,9 @@ def _generate_subsection( self, subsection: Subsection ) -> tuple[List[str], List[str]]: """ - Generate code to render components (plots, dataframes, markdown) in the given subsection, - creating imports and content for the subsection based on the component type. + Generate code to render components (plots, dataframes, markdown) in the given + subsection, creating imports and content for the subsection based on + the component type. Parameters ---------- From f7f26d350fff2a320824125ab04ce048563bdd6e Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Fri, 20 Jun 2025 09:57:12 +0200 Subject: [PATCH 12/41] :wrench: add jupytext configuration for notebooks to project --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7a886d1..961cc90 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,3 +74,6 @@ vuegen = "vuegen.__main__:main" [tool.isort] profile = "black" + +[tool.jupytext] +formats = "ipynb,py:percent" From 1d9855ef913fccb1aa2a041bbdb5a48dade1747d Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Wed, 25 Jun 2025 14:42:02 +0200 Subject: [PATCH 13/41] =?UTF-8?q?=E2=9C=85=20update=20example=20report=20f?= =?UTF-8?q?iles=20so=20test=20pass=20(reviewed=20changes=20are=20only=20st?= =?UTF-8?q?yle=20of=20.py=20files)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/vuegen/streamlit_reportview.py | 4 +- .../sections/Dataframes/All_Formats.py | 119 ++++++++++++++---- .../sections/Home/Homepage.py | 10 +- .../sections/Html/All_Html.py | 44 ++++++- .../sections/Markdown/All_Markdown.py | 18 ++- .../sections/Networks/Interactive_Networks.py | 45 +++++-- .../sections/Networks/Static_Networks.py | 20 ++- .../sections/Plots/Interactive_Plots.py | 112 ++++++++++++++--- .../sections/Plots/Static_Plots.py | 42 +++++-- .../sections/report_manager.py | 18 ++- 10 files changed, 358 insertions(+), 74 deletions(-) diff --git a/src/vuegen/streamlit_reportview.py b/src/vuegen/streamlit_reportview.py index ea63b09..f7a7fca 100644 --- a/src/vuegen/streamlit_reportview.py +++ b/src/vuegen/streamlit_reportview.py @@ -678,10 +678,10 @@ def _generate_plot_content(self, plot) -> List[str]: textwrap.dedent( f""" st.markdown(("

" - f"Number of nodes: {num_nodes}

"), + "Number of nodes: {num_nodes}

"), unsafe_allow_html=True) st.markdown(("

" - f" Number of relationships: {num_edges}

"), + " Number of relationships: {num_edges}

"), unsafe_allow_html=True) """ ) diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/streamlit_report/sections/Dataframes/All_Formats.py b/tests/report_examples/Basic_example_vuegen_demo_notebook/streamlit_report/sections/Dataframes/All_Formats.py index 5d60849..1c70f5d 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/streamlit_report/sections/Dataframes/All_Formats.py +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/streamlit_report/sections/Dataframes/All_Formats.py @@ -4,17 +4,38 @@ import streamlit as st df_index = 1 -st.markdown('''

All Formats

''', unsafe_allow_html=True) -st.markdown('''

Phyla Correlation Network Csv

''', unsafe_allow_html=True) + +st.markdown( + ( + "

All Formats

" + ), + unsafe_allow_html=True) + + +st.markdown( + ( + "

Phyla Correlation Network Csv

" + ), + unsafe_allow_html=True) + df = pd.read_csv('docs/example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/1_phyla_correlation_network_csv.csv') # Displays a DataFrame using AgGrid with configurable options. grid_builder = GridOptionsBuilder.from_dataframe(df) -grid_builder.configure_default_column(editable=True, groupable=True, filter=True) -grid_builder.configure_side_bar(filters_panel=True, columns_panel=True) +grid_builder.configure_default_column(editable=True, + groupable=True, + filter=True, +) +grid_builder.configure_side_bar(filters_panel=True, + columns_panel=True) grid_builder.configure_selection(selection_mode="multiple") -grid_builder.configure_pagination(enabled=True, paginationAutoPageSize=False, paginationPageSize=20) +grid_builder.configure_pagination(enabled=True, + paginationAutoPageSize=False, + paginationPageSize=20, +) grid_options = grid_builder.build() AgGrid(df, gridOptions=grid_options, enable_enterprise_modules=True) @@ -28,7 +49,14 @@ mime='text/csv', key=f"download_button_{df_index}") df_index += 1 -st.markdown('''

Abundance Table Example Xls

''', unsafe_allow_html=True) + +st.markdown( + ( + "

Abundance Table Example Xls

" + ), + unsafe_allow_html=True) + selected_sheet = 0 sheet_names = table_utils.get_sheet_names("docs/example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/2_abundance_table_example_xls.xls") selected_sheet = st.selectbox("Select a sheet to display", options=sheet_names) @@ -38,10 +66,17 @@ # Displays a DataFrame using AgGrid with configurable options. grid_builder = GridOptionsBuilder.from_dataframe(df) -grid_builder.configure_default_column(editable=True, groupable=True, filter=True) -grid_builder.configure_side_bar(filters_panel=True, columns_panel=True) +grid_builder.configure_default_column(editable=True, + groupable=True, + filter=True, +) +grid_builder.configure_side_bar(filters_panel=True, + columns_panel=True) grid_builder.configure_selection(selection_mode="multiple") -grid_builder.configure_pagination(enabled=True, paginationAutoPageSize=False, paginationPageSize=20) +grid_builder.configure_pagination(enabled=True, + paginationAutoPageSize=False, + paginationPageSize=20, +) grid_options = grid_builder.build() AgGrid(df, gridOptions=grid_options, enable_enterprise_modules=True) @@ -55,16 +90,30 @@ mime='text/csv', key=f"download_button_{df_index}") df_index += 1 -st.markdown('''

Sample Info Example Txt

''', unsafe_allow_html=True) + +st.markdown( + ( + "

Sample Info Example Txt

" + ), + unsafe_allow_html=True) + df = pd.read_table('docs/example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/3_sample_info_example_txt.txt') # Displays a DataFrame using AgGrid with configurable options. grid_builder = GridOptionsBuilder.from_dataframe(df) -grid_builder.configure_default_column(editable=True, groupable=True, filter=True) -grid_builder.configure_side_bar(filters_panel=True, columns_panel=True) +grid_builder.configure_default_column(editable=True, + groupable=True, + filter=True, +) +grid_builder.configure_side_bar(filters_panel=True, + columns_panel=True) grid_builder.configure_selection(selection_mode="multiple") -grid_builder.configure_pagination(enabled=True, paginationAutoPageSize=False, paginationPageSize=20) +grid_builder.configure_pagination(enabled=True, + paginationAutoPageSize=False, + paginationPageSize=20, +) grid_options = grid_builder.build() AgGrid(df, gridOptions=grid_options, enable_enterprise_modules=True) @@ -78,16 +127,30 @@ mime='text/csv', key=f"download_button_{df_index}") df_index += 1 -st.markdown('''

Sample Info Example Parquet

''', unsafe_allow_html=True) + +st.markdown( + ( + "

Sample Info Example Parquet

" + ), + unsafe_allow_html=True) + df = pd.read_parquet('docs/example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/4_sample_info_example_parquet.parquet') # Displays a DataFrame using AgGrid with configurable options. grid_builder = GridOptionsBuilder.from_dataframe(df) -grid_builder.configure_default_column(editable=True, groupable=True, filter=True) -grid_builder.configure_side_bar(filters_panel=True, columns_panel=True) +grid_builder.configure_default_column(editable=True, + groupable=True, + filter=True, +) +grid_builder.configure_side_bar(filters_panel=True, + columns_panel=True) grid_builder.configure_selection(selection_mode="multiple") -grid_builder.configure_pagination(enabled=True, paginationAutoPageSize=False, paginationPageSize=20) +grid_builder.configure_pagination(enabled=True, + paginationAutoPageSize=False, + paginationPageSize=20, +) grid_options = grid_builder.build() AgGrid(df, gridOptions=grid_options, enable_enterprise_modules=True) @@ -101,17 +164,31 @@ mime='text/csv', key=f"download_button_{df_index}") df_index += 1 -st.markdown('''

Example Xlsx

''', unsafe_allow_html=True) + +st.markdown( + ( + "

Example Xlsx

" + ), + unsafe_allow_html=True) + selected_sheet = 0 df = pd.read_excel('docs/example_data/Basic_example_vuegen_demo_notebook/2_Dataframes/1_All_formats/5_example_xlsx.xlsx', sheet_name=selected_sheet) # Displays a DataFrame using AgGrid with configurable options. grid_builder = GridOptionsBuilder.from_dataframe(df) -grid_builder.configure_default_column(editable=True, groupable=True, filter=True) -grid_builder.configure_side_bar(filters_panel=True, columns_panel=True) +grid_builder.configure_default_column(editable=True, + groupable=True, + filter=True, +) +grid_builder.configure_side_bar(filters_panel=True, + columns_panel=True) grid_builder.configure_selection(selection_mode="multiple") -grid_builder.configure_pagination(enabled=True, paginationAutoPageSize=False, paginationPageSize=20) +grid_builder.configure_pagination(enabled=True, + paginationAutoPageSize=False, + paginationPageSize=20, +) grid_options = grid_builder.build() AgGrid(df, gridOptions=grid_options, enable_enterprise_modules=True) diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/streamlit_report/sections/Home/Homepage.py b/tests/report_examples/Basic_example_vuegen_demo_notebook/streamlit_report/sections/Home/Homepage.py index e35e50f..54b18c0 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/streamlit_report/sections/Home/Homepage.py +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/streamlit_report/sections/Home/Homepage.py @@ -1,6 +1,12 @@ import streamlit as st -st.markdown('''

A general description of the report. -

''', unsafe_allow_html=True) + +st.markdown( + ( + "

A general description of the report.

" + ), + unsafe_allow_html=True) + footer = ''' -include-after-body: - text: | - """, - r.ReportType.PDF: """ - pdf: - toc: false - fig-align: center - margin: - - bottom=40mm - include-in-header: - text: | - \\usepackage{scrlayer-scrpage} - \\usepackage{hyperref} - \\clearpairofpagestyles - \\lofoot{This report was generated with \\href{https://github.com/Multiomics-Analytics-Group/vuegen}{VueGen} | \\copyright{} 2025 \\href{https://github.com/Multiomics-Analytics-Group}{Multiomics Network Analytics Group}} - \\rofoot{\\pagemark}""", - r.ReportType.DOCX: """ - docx: - toc: false""", - r.ReportType.ODT: """ - odt: - toc: false""", - r.ReportType.REVEALJS: """ - revealjs: - toc: false - smaller: true - controls: true - navigation-mode: vertical - controls-layout: bottom-right - output-file: quarto_report_revealjs.html -include-in-header: - text: | - -include-after-body: - text: | - """, - r.ReportType.PPTX: """ - pptx: - toc: false - output: true""", - r.ReportType.JUPYTER: """ - html: - toc: true - toc-location: left - toc-depth: 3 - page-layout: full - self-contained: true -include-in-header: - text: | - -include-after-body: - text: | - """, + r.ReportType.HTML: textwrap.dedent( + """ + html: + toc: true + toc-location: left + toc-depth: 3 + page-layout: full + self-contained: true + include-in-header: + text: | + + include-after-body: + text: | + """ + ), + r.ReportType.PDF: textwrap.indent( + textwrap.dedent( + """ + pdf: + toc: false + fig-align: center + margin: + - bottom=40mm + include-in-header: + text: | + \\usepackage{scrlayer-scrpage} + \\usepackage{hyperref} + \\clearpairofpagestyles + \\lofoot{This report was generated with \\href{https://github.com/Multiomics-Analytics-Group/vuegen}{VueGen} | \\copyright{} 2025 \\href{https://github.com/Multiomics-Analytics-Group}{Multiomics Network Analytics Group}} + \\rofoot{\\pagemark}""" + ), + " ", + ), + r.ReportType.DOCX: textwrap.indent( + textwrap.dedent( + """ + docx: + toc: false""" + ), + " ", + ), + r.ReportType.ODT: textwrap.indent( + textwrap.dedent( + """ + odt: + toc: false""" + ), + " ", + ), + r.ReportType.REVEALJS: textwrap.dedent( + """ + revealjs: + toc: false + smaller: true + controls: true + navigation-mode: vertical + controls-layout: bottom-right + output-file: quarto_report_revealjs.html + include-in-header: + text: | + + include-after-body: + text: | + """ + ), + r.ReportType.PPTX: textwrap.indent( + textwrap.dedent( + """ + pptx: + toc: false + output: true""" + ), + " ", + ), + r.ReportType.JUPYTER: textwrap.dedent( + """ + html: + toc: true + toc-location: left + toc-depth: 3 + page-layout: full + self-contained: true + include-in-header: + text: | + + include-after-body: + text: | + """ + ), } # Create a key based on the report type and format key = self.report_type @@ -632,41 +660,57 @@ def _generate_plot_code(self, plot, output_file="") -> str: The generated plot code as a string. """ # Initialize plot code with common structure - plot_code = f"""```{{python}} -#| label: '{plot.title} {plot.id}' -#| fig-cap: "" -""" + plot_code = textwrap.dedent( + f""" + ```{{python}} + #| label: '{plot.title} {plot.id}' + #| fig-cap: "" + """ + ) # If the file path is a URL, generate code to fetch content via requests if is_url(plot.file_path): - plot_code += f""" -response = requests.get('{plot.file_path}') -response.raise_for_status() -plot_json = response.text\n""" + plot_code += textwrap.dedent( + f""" + response = requests.get('{plot.file_path}') + response.raise_for_status() + plot_json = response.text + """ + ) else: # If it's a local file plot_rel_path = get_relative_file_path( plot.file_path, relative_to=self.output_dir ).as_posix() - plot_code += f""" + plot_code += textwrap.dedent( + f""" with open(report_dir /'{plot_rel_path}', 'r') as plot_file: - plot_json = json.load(plot_file)\n""" + plot_json = json.load(plot_file) +""" + ) # Add specific code for each visualization tool if plot.plot_type == r.PlotType.PLOTLY: - plot_code += """ -# Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']}\n -# Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])]\n -# Convert JSON to string -plot_json_str = json.dumps(plot_json)\n -# Create the plotly plot -fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50))\n""" + plot_code += textwrap.dedent( + """ + # Keep only 'data' and 'layout' sections + plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']}\n + # Remove 'frame' section in 'data' + plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])]\n + # Convert JSON to string + plot_json_str = json.dumps(plot_json)\n + # Create the plotly plot + fig_plotly = pio.from_json(plot_json_str) + fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) + """ + ) elif plot.plot_type == r.PlotType.ALTAIR: - plot_code += """ -# Convert JSON to string -plot_json_str = json.dumps(plot_json)\n -# Create the plotly plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370)\n""" + plot_code += textwrap.dedent( + """ + # Convert JSON to string + plot_json_str = json.dumps(plot_json) + + # Create the altair plot + fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) + """ + ) elif plot.plot_type == r.PlotType.INTERACTIVE_NETWORK: # Generate the HTML embedding for interactive networks if is_url(plot.file_path) and plot.file_path.endswith(".html"): @@ -677,10 +721,13 @@ def _generate_plot_code(self, plot, output_file="") -> str: ) # Embed the HTML file in an iframe - plot_code = f""" -
- -
\n""" + plot_code = textwrap.dedent( + f""" +
+ +
+ """ + ) return plot_code def _generate_dataframe_content(self, dataframe) -> List[str]: @@ -942,10 +989,14 @@ def _generate_html_content(self, html) -> List[str]: html_file_path = get_relative_file_path( html.file_path, relative_to=self.output_dir ) - iframe_code = f""" -
- -
\n""" + iframe_code = textwrap.dedent( + f""" +
+ +
+ """ + ) html_content.append(iframe_code) except Exception as e: diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/docx/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/docx/quarto_report/quarto_report.qmd index 8136c42..fe88ba3 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/docx/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/docx/quarto_report/quarto_report.qmd @@ -33,6 +33,7 @@ Optional description for section. ### Top Species Plot By Biome Plotly + ```{python} #| label: 'Top Species Plot By Biome Plotly 1' #| fig-cap: "" @@ -59,6 +60,7 @@ fig_plotly.write_image("static/Top_Species_Plot_By_Biome_Plotly.png") ![](static/Top_Species_Plot_By_Biome_Plotly.png){fig-alt= width=90%} ### Multiline Plot Altair + ```{python} #| label: 'Multiline Plot Altair 2' #| fig-cap: "" @@ -69,7 +71,7 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair.save("static/Multiline_Plot_Altair.png") @@ -78,6 +80,7 @@ fig_altair.save("static/Multiline_Plot_Altair.png") ![](static/Multiline_Plot_Altair.png){fig-alt= width=90%} ### Pie Plot Countries Plotly + ```{python} #| label: 'Pie Plot Countries Plotly 3' #| fig-cap: "" @@ -104,6 +107,7 @@ fig_plotly.write_image("static/Pie_Plot_Countries_Plotly.png") ![](static/Pie_Plot_Countries_Plotly.png){fig-alt= width=90%} ### Pie Plots Biomes Plotly + ```{python} #| label: 'Pie Plots Biomes Plotly 4' #| fig-cap: "" @@ -130,6 +134,7 @@ fig_plotly.write_image("static/Pie_Plots_Biomes_Plotly.png") ![](static/Pie_Plots_Biomes_Plotly.png){fig-alt= width=90%} ### Saline Metagenomics Samples Map Altair + ```{python} #| label: 'Saline Metagenomics Samples Map Altair 5' #| fig-cap: "" @@ -140,7 +145,7 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") @@ -149,6 +154,7 @@ fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") ![](static/Saline_Metagenomics_Samples_Map_Altair.png){fig-alt= width=90%} ### Plotly Plot R + ```{python} #| label: 'Plotly Plot R 6' #| fig-cap: "" diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd index 415e2fb..b3bcc23 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd @@ -58,6 +58,7 @@ Optional description for section. ### Top Species Plot By Biome Plotly + ```{python} #| label: 'Top Species Plot By Biome Plotly 1' #| fig-cap: "" @@ -82,6 +83,7 @@ fig_plotly.show() ``` ### Multiline Plot Altair + ```{python} #| label: 'Multiline Plot Altair 2' #| fig-cap: "" @@ -92,13 +94,14 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair ``` ### Pie Plot Countries Plotly + ```{python} #| label: 'Pie Plot Countries Plotly 3' #| fig-cap: "" @@ -123,6 +126,7 @@ fig_plotly.show() ``` ### Pie Plots Biomes Plotly + ```{python} #| label: 'Pie Plots Biomes Plotly 4' #| fig-cap: "" @@ -147,6 +151,7 @@ fig_plotly.show() ``` ### Saline Metagenomics Samples Map Altair + ```{python} #| label: 'Saline Metagenomics Samples Map Altair 5' #| fig-cap: "" @@ -157,13 +162,14 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair ``` ### Plotly Plot R + ```{python} #| label: 'Plotly Plot R 6' #| fig-cap: "" @@ -283,7 +289,8 @@ Optional description for subsection. ### Plot
- +
### Ckg Network @@ -299,7 +306,8 @@ Optional description for subsection. ### Multiqc Report
- +
# Markdown diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd index 15a5be4..9ef1dce 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd @@ -58,6 +58,7 @@ Optional description for section. ### Top Species Plot By Biome Plotly + ```{python} #| label: 'Top Species Plot By Biome Plotly 1' #| fig-cap: "" @@ -82,6 +83,7 @@ fig_plotly.show() ``` ### Multiline Plot Altair + ```{python} #| label: 'Multiline Plot Altair 2' #| fig-cap: "" @@ -92,13 +94,14 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair ``` ### Pie Plot Countries Plotly + ```{python} #| label: 'Pie Plot Countries Plotly 3' #| fig-cap: "" @@ -123,6 +126,7 @@ fig_plotly.show() ``` ### Pie Plots Biomes Plotly + ```{python} #| label: 'Pie Plots Biomes Plotly 4' #| fig-cap: "" @@ -147,6 +151,7 @@ fig_plotly.show() ``` ### Saline Metagenomics Samples Map Altair + ```{python} #| label: 'Saline Metagenomics Samples Map Altair 5' #| fig-cap: "" @@ -157,13 +162,14 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair ``` ### Plotly Plot R + ```{python} #| label: 'Plotly Plot R 6' #| fig-cap: "" @@ -283,7 +289,8 @@ Optional description for subsection. ### Plot
- +
### Ckg Network @@ -299,7 +306,8 @@ Optional description for subsection. ### Multiqc Report
- +
# Markdown diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/odt/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/odt/quarto_report/quarto_report.qmd index 8bcea9d..d8353b9 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/odt/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/odt/quarto_report/quarto_report.qmd @@ -33,6 +33,7 @@ Optional description for section. ### Top Species Plot By Biome Plotly + ```{python} #| label: 'Top Species Plot By Biome Plotly 1' #| fig-cap: "" @@ -59,6 +60,7 @@ fig_plotly.write_image("static/Top_Species_Plot_By_Biome_Plotly.png") ![](static/Top_Species_Plot_By_Biome_Plotly.png){fig-alt= width=90%} ### Multiline Plot Altair + ```{python} #| label: 'Multiline Plot Altair 2' #| fig-cap: "" @@ -69,7 +71,7 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair.save("static/Multiline_Plot_Altair.png") @@ -78,6 +80,7 @@ fig_altair.save("static/Multiline_Plot_Altair.png") ![](static/Multiline_Plot_Altair.png){fig-alt= width=90%} ### Pie Plot Countries Plotly + ```{python} #| label: 'Pie Plot Countries Plotly 3' #| fig-cap: "" @@ -104,6 +107,7 @@ fig_plotly.write_image("static/Pie_Plot_Countries_Plotly.png") ![](static/Pie_Plot_Countries_Plotly.png){fig-alt= width=90%} ### Pie Plots Biomes Plotly + ```{python} #| label: 'Pie Plots Biomes Plotly 4' #| fig-cap: "" @@ -130,6 +134,7 @@ fig_plotly.write_image("static/Pie_Plots_Biomes_Plotly.png") ![](static/Pie_Plots_Biomes_Plotly.png){fig-alt= width=90%} ### Saline Metagenomics Samples Map Altair + ```{python} #| label: 'Saline Metagenomics Samples Map Altair 5' #| fig-cap: "" @@ -140,7 +145,7 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") @@ -149,6 +154,7 @@ fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") ![](static/Saline_Metagenomics_Samples_Map_Altair.png){fig-alt= width=90%} ### Plotly Plot R + ```{python} #| label: 'Plotly Plot R 6' #| fig-cap: "" diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd index 17900ea..d4e5d09 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd @@ -43,6 +43,7 @@ Optional description for section. ### Top Species Plot By Biome Plotly + ```{python} #| label: 'Top Species Plot By Biome Plotly 1' #| fig-cap: "" @@ -69,6 +70,7 @@ fig_plotly.write_image("static/Top_Species_Plot_By_Biome_Plotly.png") ![](static/Top_Species_Plot_By_Biome_Plotly.png){fig-alt= width=90%} ### Multiline Plot Altair + ```{python} #| label: 'Multiline Plot Altair 2' #| fig-cap: "" @@ -79,7 +81,7 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair.save("static/Multiline_Plot_Altair.png") @@ -88,6 +90,7 @@ fig_altair.save("static/Multiline_Plot_Altair.png") ![](static/Multiline_Plot_Altair.png){fig-alt= width=90%} ### Pie Plot Countries Plotly + ```{python} #| label: 'Pie Plot Countries Plotly 3' #| fig-cap: "" @@ -114,6 +117,7 @@ fig_plotly.write_image("static/Pie_Plot_Countries_Plotly.png") ![](static/Pie_Plot_Countries_Plotly.png){fig-alt= width=90%} ### Pie Plots Biomes Plotly + ```{python} #| label: 'Pie Plots Biomes Plotly 4' #| fig-cap: "" @@ -140,6 +144,7 @@ fig_plotly.write_image("static/Pie_Plots_Biomes_Plotly.png") ![](static/Pie_Plots_Biomes_Plotly.png){fig-alt= width=90%} ### Saline Metagenomics Samples Map Altair + ```{python} #| label: 'Saline Metagenomics Samples Map Altair 5' #| fig-cap: "" @@ -150,7 +155,7 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") @@ -159,6 +164,7 @@ fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") ![](static/Saline_Metagenomics_Samples_Map_Altair.png){fig-alt= width=90%} ### Plotly Plot R + ```{python} #| label: 'Plotly Plot R 6' #| fig-cap: "" diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/pptx/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/pptx/quarto_report/quarto_report.qmd index a7db1d1..05d9afe 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/pptx/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/pptx/quarto_report/quarto_report.qmd @@ -34,6 +34,7 @@ Optional description for section. ### Top Species Plot By Biome Plotly + ```{python} #| label: 'Top Species Plot By Biome Plotly 1' #| fig-cap: "" @@ -60,6 +61,7 @@ fig_plotly.write_image("static/Top_Species_Plot_By_Biome_Plotly.png") ![](static/Top_Species_Plot_By_Biome_Plotly.png){fig-alt= width=90%} ### Multiline Plot Altair + ```{python} #| label: 'Multiline Plot Altair 2' #| fig-cap: "" @@ -70,7 +72,7 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair.save("static/Multiline_Plot_Altair.png") @@ -79,6 +81,7 @@ fig_altair.save("static/Multiline_Plot_Altair.png") ![](static/Multiline_Plot_Altair.png){fig-alt= width=90%} ### Pie Plot Countries Plotly + ```{python} #| label: 'Pie Plot Countries Plotly 3' #| fig-cap: "" @@ -105,6 +108,7 @@ fig_plotly.write_image("static/Pie_Plot_Countries_Plotly.png") ![](static/Pie_Plot_Countries_Plotly.png){fig-alt= width=90%} ### Pie Plots Biomes Plotly + ```{python} #| label: 'Pie Plots Biomes Plotly 4' #| fig-cap: "" @@ -131,6 +135,7 @@ fig_plotly.write_image("static/Pie_Plots_Biomes_Plotly.png") ![](static/Pie_Plots_Biomes_Plotly.png){fig-alt= width=90%} ### Saline Metagenomics Samples Map Altair + ```{python} #| label: 'Saline Metagenomics Samples Map Altair 5' #| fig-cap: "" @@ -141,7 +146,7 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") @@ -150,6 +155,7 @@ fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") ![](static/Saline_Metagenomics_Samples_Map_Altair.png){fig-alt= width=90%} ### Plotly Plot R + ```{python} #| label: 'Plotly Plot R 6' #| fig-cap: "" diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd index d3ba678..8ace56f 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd @@ -61,6 +61,7 @@ Optional description for section. ::: {.panel-tabset} ### Top Species Plot By Biome Plotly + ```{python} #| label: 'Top Species Plot By Biome Plotly 1' #| fig-cap: "" @@ -85,6 +86,7 @@ fig_plotly.show() ``` ### Multiline Plot Altair + ```{python} #| label: 'Multiline Plot Altair 2' #| fig-cap: "" @@ -95,13 +97,14 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair ``` ### Pie Plot Countries Plotly + ```{python} #| label: 'Pie Plot Countries Plotly 3' #| fig-cap: "" @@ -126,6 +129,7 @@ fig_plotly.show() ``` ### Pie Plots Biomes Plotly + ```{python} #| label: 'Pie Plots Biomes Plotly 4' #| fig-cap: "" @@ -150,6 +154,7 @@ fig_plotly.show() ``` ### Saline Metagenomics Samples Map Altair + ```{python} #| label: 'Saline Metagenomics Samples Map Altair 5' #| fig-cap: "" @@ -160,13 +165,14 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair ``` ### Plotly Plot R + ```{python} #| label: 'Plotly Plot R 6' #| fig-cap: "" @@ -306,7 +312,8 @@ Optional description for subsection. ### Plot
- +
### Ckg Network @@ -322,7 +329,8 @@ Optional description for subsection. ### Multiqc Report
- +
::: diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd index 54cb87e..e75c635 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd @@ -60,6 +60,7 @@ This section contains example plots. Optional description for section. ### Top Species Plot By Biome Plotly + ```{python} #| label: 'Top Species Plot By Biome Plotly 1' #| fig-cap: "" @@ -84,6 +85,7 @@ fig_plotly.show() ``` ### Multiline Plot Altair + ```{python} #| label: 'Multiline Plot Altair 2' #| fig-cap: "" @@ -94,13 +96,14 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair ``` ### Pie Plot Countries Plotly + ```{python} #| label: 'Pie Plot Countries Plotly 3' #| fig-cap: "" @@ -125,6 +128,7 @@ fig_plotly.show() ``` ### Pie Plots Biomes Plotly + ```{python} #| label: 'Pie Plots Biomes Plotly 4' #| fig-cap: "" @@ -149,6 +153,7 @@ fig_plotly.show() ``` ### Saline Metagenomics Samples Map Altair + ```{python} #| label: 'Saline Metagenomics Samples Map Altair 5' #| fig-cap: "" @@ -159,7 +164,7 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem # Convert JSON to string plot_json_str = json.dumps(plot_json) -# Create the plotly plot +# Create the altair plot fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) fig_altair @@ -257,7 +262,8 @@ Optional description for subsection ### Plot
- +
### Ckg Network @@ -273,7 +279,8 @@ Optional description for subsection ### Multiqc Report
- +
# Markdown From 206deab652bfaf0e26e0eef988c743360ea87dd1 Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Wed, 25 Jun 2025 17:06:51 +0200 Subject: [PATCH 20/41] :art: keep urls at the top - maybe 'Verschlimmbessern': footer can be hard to read (pdf) - needed to use escaping of {} using unicode: \u007b is { and \u007d is } - :bug: jupyter used relative import -> changed to URL link of LOGO - unify MONA mentioning (pdf) --- src/vuegen/quarto_reportview.py | 64 ++++++++++++------- .../html/quarto_report/quarto_report.qmd | 3 +- .../jupyter/quarto_report/quarto_report.qmd | 7 +- .../pdf/quarto_report/quarto_report.qmd | 4 +- .../revealjs/quarto_report/quarto_report.qmd | 3 +- .../html/quarto_report/quarto_report.qmd | 3 +- 6 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/vuegen/quarto_reportview.py b/src/vuegen/quarto_reportview.py index 21cda55..d2aeaa0 100644 --- a/src/vuegen/quarto_reportview.py +++ b/src/vuegen/quarto_reportview.py @@ -1,3 +1,5 @@ +"""QuartoReportView class for generating Quarto reports.""" + import os import subprocess import sys @@ -11,6 +13,15 @@ from . import table_utils from .utils import create_folder, get_relative_file_path, is_url, sort_imports +GITHUB_ORG_URL = "https://github.com/Multiomics-Analytics-Group" +ORG = "Multiomics Network Analytics Group (MoNA)" +GITHUB_ORG_URL_BRACKETS = "{https://github.com/Multiomics-Analytics-Group}" +REPO_URL = "https://github.com/Multiomics-Analytics-Group/vuegen" +LOGO_URL = ( + "https://raw.githubusercontent.com/Multiomics-Analytics-Group/" + "vuegen/main/docs/images/vuegen_logo.svg" +) + class QuartoReportView(r.ReportView): """ @@ -321,9 +332,10 @@ def _create_yaml_header(self) -> str: format:""" ) # Define format-specific YAML configurations + # \u007b is { and \u007d is } format_configs = { r.ReportType.HTML: textwrap.dedent( - """ + f""" html: toc: true toc-location: left @@ -333,27 +345,29 @@ def _create_yaml_header(self) -> str: include-in-header: text: | include-after-body: text: | """ ), + # \u007b is { and \u007d is } r.ReportType.PDF: textwrap.indent( textwrap.dedent( - """ + f""" pdf: toc: false fig-align: center @@ -361,11 +375,13 @@ def _create_yaml_header(self) -> str: - bottom=40mm include-in-header: text: | - \\usepackage{scrlayer-scrpage} - \\usepackage{hyperref} + \\usepackage{{scrlayer-scrpage}} + \\usepackage{{hyperref}} \\clearpairofpagestyles - \\lofoot{This report was generated with \\href{https://github.com/Multiomics-Analytics-Group/vuegen}{VueGen} | \\copyright{} 2025 \\href{https://github.com/Multiomics-Analytics-Group}{Multiomics Network Analytics Group}} - \\rofoot{\\pagemark}""" + \\lofoot\u007bThis report was generated with + \\href{{{REPO_URL}}}{{VueGen}} | \\copyright{{}} 2025 + \\href{GITHUB_ORG_URL_BRACKETS}\u007b{ORG}\u007d\u007d + \\rofoot{{\\pagemark}}""" ), " ", ), @@ -386,7 +402,7 @@ def _create_yaml_header(self) -> str: " ", ), r.ReportType.REVEALJS: textwrap.dedent( - """ + f""" revealjs: toc: false smaller: true @@ -397,22 +413,23 @@ def _create_yaml_header(self) -> str: include-in-header: text: | include-after-body: text: | """ ), r.ReportType.PPTX: textwrap.indent( @@ -425,7 +442,7 @@ def _create_yaml_header(self) -> str: " ", ), r.ReportType.JUPYTER: textwrap.dedent( - """ + f""" html: toc: true toc-location: left @@ -435,22 +452,23 @@ def _create_yaml_header(self) -> str: include-in-header: text: | include-after-body: text: | """ ), } diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd index b3bcc23..3c962fe 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd @@ -30,7 +30,8 @@ include-after-body: VueGen - | Copyright 2025 Multiomics Network Analytics Group (MoNA) + | Copyright 2025 + Multiomics Network Analytics Group (MoNA) --- diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd index 9ef1dce..642d2d9 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd @@ -21,16 +21,17 @@ include-in-header: width: 100%; text-align: center; margin-top: 20px; - } + } include-after-body: text: | --- diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd index d4e5d09..5bc0532 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd @@ -16,7 +16,9 @@ format: \usepackage{scrlayer-scrpage} \usepackage{hyperref} \clearpairofpagestyles - \lofoot{This report was generated with \href{https://github.com/Multiomics-Analytics-Group/vuegen}{VueGen} | \copyright{} 2025 \href{https://github.com/Multiomics-Analytics-Group}{Multiomics Network Analytics Group}} + \lofoot{This report was generated with + \href{https://github.com/Multiomics-Analytics-Group/vuegen}{VueGen} | \copyright{} 2025 + \href{https://github.com/Multiomics-Analytics-Group}{Multiomics Network Analytics Group (MoNA)}} \rofoot{\pagemark} --- diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd index 8ace56f..6e6e974 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd @@ -31,7 +31,8 @@ include-after-body: VueGen - | Copyright 2025 Multiomics Network Analytics Group (MoNA) + | Copyright 2025 Multiomics Network Analytics Group (MoNA) --- diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd index e75c635..06de8a5 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd @@ -30,7 +30,8 @@ include-after-body: VueGen - | Copyright 2025 Multiomics Network Analytics Group (MoNA) + | Copyright 2025 + Multiomics Network Analytics Group (MoNA) --- From ed1132d11bb549555f27479ddb65e4f6cc88c585 Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Wed, 25 Jun 2025 17:18:14 +0200 Subject: [PATCH 21/41] :art: limit remaining lines to 90 characters --- src/vuegen/quarto_reportview.py | 21 ++++-- .../docx/quarto_report/quarto_report.qmd | 66 ++++++++++------- .../html/quarto_report/quarto_report.qmd | 74 ++++++++++++------- .../jupyter/quarto_report/quarto_report.qmd | 74 ++++++++++++------- .../odt/quarto_report/quarto_report.qmd | 66 ++++++++++------- .../pdf/quarto_report/quarto_report.qmd | 66 ++++++++++------- .../pptx/quarto_report/quarto_report.qmd | 66 ++++++++++------- .../revealjs/quarto_report/quarto_report.qmd | 74 ++++++++++++------- .../html/quarto_report/quarto_report.qmd | 59 +++++++++------ 9 files changed, 350 insertions(+), 216 deletions(-) diff --git a/src/vuegen/quarto_reportview.py b/src/vuegen/quarto_reportview.py index d2aeaa0..e9d7dea 100644 --- a/src/vuegen/quarto_reportview.py +++ b/src/vuegen/quarto_reportview.py @@ -709,14 +709,20 @@ def _generate_plot_code(self, plot, output_file="") -> str: plot_code += textwrap.dedent( """ # Keep only 'data' and 'layout' sections - plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']}\n + plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' - plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])]\n + plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string - plot_json_str = json.dumps(plot_json)\n + plot_json_str = json.dumps(plot_json) # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) - fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) + fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) """ ) elif plot.plot_type == r.PlotType.ALTAIR: @@ -726,7 +732,8 @@ def _generate_plot_code(self, plot, output_file="") -> str: plot_json_str = json.dumps(plot_json) # Create the altair plot - fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) + fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) """ ) elif plot.plot_type == r.PlotType.INTERACTIVE_NETWORK: @@ -742,7 +749,9 @@ def _generate_plot_code(self, plot, output_file="") -> str: plot_code = textwrap.dedent( f"""
- +
""" ) diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/docx/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/docx/quarto_report/quarto_report.qmd index fe88ba3..2201667 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/docx/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/docx/quarto_report/quarto_report.qmd @@ -42,17 +42,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Top_Species_Plot_By_Biome_Plotly.png") ``` @@ -72,7 +75,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair.save("static/Multiline_Plot_Altair.png") ``` @@ -89,17 +93,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Pie_Plot_Countries_Plotly.png") ``` @@ -116,17 +123,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Pie_Plots_Biomes_Plotly.png") ``` @@ -146,7 +156,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") ``` @@ -163,17 +174,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Plotly_Plot_R.png") ``` diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd index 3c962fe..0a3a5e3 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/html/quarto_report/quarto_report.qmd @@ -68,17 +68,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -96,7 +99,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair ``` @@ -111,17 +115,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -136,17 +143,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -164,7 +174,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair ``` @@ -179,17 +190,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -278,7 +292,9 @@ Optional description for subsection.
- +
## Static Networks @@ -301,7 +317,9 @@ Optional description for subsection.
- +
### Multiqc Report diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd index 642d2d9..c9e6638 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/jupyter/quarto_report/quarto_report.qmd @@ -68,17 +68,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -96,7 +99,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair ``` @@ -111,17 +115,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -136,17 +143,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -164,7 +174,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair ``` @@ -179,17 +190,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -278,7 +292,9 @@ Optional description for subsection.
- +
## Static Networks @@ -301,7 +317,9 @@ Optional description for subsection.
- +
### Multiqc Report diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/odt/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/odt/quarto_report/quarto_report.qmd index d8353b9..bc4151c 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/odt/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/odt/quarto_report/quarto_report.qmd @@ -42,17 +42,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Top_Species_Plot_By_Biome_Plotly.png") ``` @@ -72,7 +75,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair.save("static/Multiline_Plot_Altair.png") ``` @@ -89,17 +93,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Pie_Plot_Countries_Plotly.png") ``` @@ -116,17 +123,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Pie_Plots_Biomes_Plotly.png") ``` @@ -146,7 +156,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") ``` @@ -163,17 +174,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Plotly_Plot_R.png") ``` diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd index 5bc0532..edf136b 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/pdf/quarto_report/quarto_report.qmd @@ -54,17 +54,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Top_Species_Plot_By_Biome_Plotly.png") ``` @@ -84,7 +87,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair.save("static/Multiline_Plot_Altair.png") ``` @@ -101,17 +105,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Pie_Plot_Countries_Plotly.png") ``` @@ -128,17 +135,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Pie_Plots_Biomes_Plotly.png") ``` @@ -158,7 +168,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") ``` @@ -175,17 +186,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Plotly_Plot_R.png") ``` diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/pptx/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/pptx/quarto_report/quarto_report.qmd index 05d9afe..29d2c4f 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/pptx/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/pptx/quarto_report/quarto_report.qmd @@ -43,17 +43,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Top_Species_Plot_By_Biome_Plotly.png") ``` @@ -73,7 +76,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair.save("static/Multiline_Plot_Altair.png") ``` @@ -90,17 +94,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Pie_Plot_Countries_Plotly.png") ``` @@ -117,17 +124,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Pie_Plots_Biomes_Plotly.png") ``` @@ -147,7 +157,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair.save("static/Saline_Metagenomics_Samples_Map_Altair.png") ``` @@ -164,17 +175,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.write_image("static/Plotly_Plot_R.png") ``` diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd index 6e6e974..23328cc 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook/revealjs/quarto_report/quarto_report.qmd @@ -71,17 +71,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -99,7 +102,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair ``` @@ -114,17 +118,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -139,17 +146,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -167,7 +177,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair ``` @@ -182,17 +193,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -293,7 +307,9 @@ Optional description for subsection.
- +
::: @@ -324,7 +340,9 @@ Optional description for subsection.
- +
### Multiqc Report diff --git a/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd b/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd index 06de8a5..121e2d2 100644 --- a/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd +++ b/tests/report_examples/Basic_example_vuegen_demo_notebook_cfg/html/quarto_report/quarto_report.qmd @@ -70,17 +70,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -98,7 +101,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair ``` @@ -113,17 +117,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -138,17 +145,20 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json = json.load(plot_file) # Keep only 'data' and 'layout' sections -plot_json = {key: plot_json[key] for key in plot_json if key in ['data', 'layout']} - +plot_json = {key: plot_json[key] for key in plot_json + if key in ['data', 'layout'] + } # Remove 'frame' section in 'data' -plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} for entry in plot_json.get('data', [])] - +plot_json['data'] = [{k: v for k, v in entry.items() if k != 'frame'} + for entry in plot_json.get('data', []) + ] # Convert JSON to string plot_json_str = json.dumps(plot_json) - # Create the plotly plot fig_plotly = pio.from_json(plot_json_str) -fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50)) +fig_plotly.update_layout(autosize=False, width=950, height=400, + margin=dict(b=50, t=50, l=50, r=50) + ) fig_plotly.show() ``` @@ -166,7 +176,8 @@ with open(report_dir /'../../../../../docs/example_data/Basic_example_vuegen_dem plot_json_str = json.dumps(plot_json) # Create the altair plot -fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370) +fig_altair = alt.Chart.from_json(plot_json_str + ).properties(width=900, height=370) fig_altair ``` @@ -251,7 +262,9 @@ Optional description for subsection
- +
## Static Networks @@ -274,7 +287,9 @@ Optional description for subsection
- +
### Multiqc Report From 518ff91e05b02dd6b534d594fa185543a67f7d73 Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 26 Jun 2025 11:29:42 +0200 Subject: [PATCH 22/41] :art: shorten docstrings and comments, implement lazy logging lazy logging is recommended (only a warning): https://pylint.readthedocs.io/en/latest/user_guide/messages/warning/logging-fstring-interpolation.html --- src/vuegen/config_manager.py | 47 ++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/vuegen/config_manager.py b/src/vuegen/config_manager.py index d3a613b..6df2dee 100644 --- a/src/vuegen/config_manager.py +++ b/src/vuegen/config_manager.py @@ -1,3 +1,7 @@ +"""ConfigManage creates configuration files from folders and can create components +for reports from YAML config files. +""" + import json import logging import os @@ -10,7 +14,8 @@ class ConfigManager: """ - Class for handling metadata of reports from YAML config file and creating report objects. + Class for handling metadata of reports from YAML config file and creating report + objects. """ def __init__(self, logger: Optional[logging.Logger] = None, max_depth: int = 2): @@ -20,10 +25,11 @@ def __init__(self, logger: Optional[logging.Logger] = None, max_depth: int = 2): Parameters ---------- logger : logging.Logger, optional - A logger instance for the class. If not provided, a default logger will be created. + 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 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: @@ -53,7 +59,8 @@ def _create_title_fromdir(self, file_dirname: str) -> str: def _create_component_config_fromfile(self, file_path: Path) -> Dict[str, str]: """ - Infers a component config from a file, including component type, plot type, and additional fields. + Infers a component config from a file, including component type, plot type, + and additional fields. Parameters ---------- @@ -144,13 +151,16 @@ def _create_component_config_fromfile(self, file_path: Path) -> Dict[str, str]: else: component_config["plot_type"] = r.PlotType.PLOTLY.value except Exception as e: - self.logger.warning(f"Could not parse JSON file {file_path}: {e}") + self.logger.warning("Could not parse JSON file %s: %s", file_path, e) component_config["plot_type"] = "unknown" elif file_ext == ".md": component_config["component_type"] = r.ComponentType.MARKDOWN.value else: + if not file_ext: + # hidden files starting with a dot + file_ext = file_path.name self.logger.error( - f"Unsupported file extension: {file_ext}. Skipping file: {file_path}" + "Unsupported file extension: %s. Skipping file: %s", file_ext, file_path ) return None @@ -158,7 +168,8 @@ def _create_component_config_fromfile(self, file_path: Path) -> Dict[str, str]: def _sort_paths_by_numprefix(self, paths: List[Path]) -> List[Path]: """ - Sorts a list of Paths by numeric prefixes in their names, placing non-numeric items at the end. + Sorts a list of Paths by numeric prefixes in their names, placing non-numeric + items at the end. Parameters ---------- @@ -298,7 +309,8 @@ def create_yamlconfig_fromdir( self, base_dir: str ) -> Tuple[Dict[str, Union[str, List[Dict]]], Path]: """ - Generates a YAML-compatible config file from a directory. It also returns the resolved folder path. + Generates a YAML-compatible config file from a directory. It also returns the + resolved folder path. Parameters ---------- @@ -361,7 +373,8 @@ def create_yamlconfig_fromdir( def initialize_report(self, config: dict) -> tuple[r.Report, dict]: """ - Extracts report metadata from a YAML config file and returns a Report object and the raw metadata. + Extracts report metadata from a YAML config file and returns a Report object and + the raw metadata. Parameters ---------- @@ -371,7 +384,8 @@ def initialize_report(self, config: dict) -> tuple[r.Report, dict]: Returns ------- report, config : tuple[Report, dict] - A tuple containing the Report object created from the YAML config file and the raw metadata dictionary. + A tuple containing the Report object created from the YAML config file and + the raw metadata dictionary. Raises ------ @@ -396,7 +410,9 @@ def initialize_report(self, config: dict) -> tuple[r.Report, dict]: report.sections.append(section) self.logger.info( - f"Report '{report.title}' initialized with {len(report.sections)} sections." + "Report '%s' initialized with %d sections.", + report.title, + len(report.sections), ) return report, config @@ -472,7 +488,8 @@ def _create_component(self, component_data: dict) -> r.Component: Returns ------- Component - A Component object (Plot, DataFrame, or Markdown) populated with the provided metadata. + A Component object (Plot, DataFrame, or Markdown) populated with the provided + metadata. """ # Determine the component type component_type = assert_enum_value( @@ -620,8 +637,8 @@ def _create_apicall_component(self, component_data: dict) -> r.APICall: try: parsed_body = json.loads(request_body) except json.JSONDecodeError as e: - self.logger.error(f"Failed to parse request_body JSON: {e}") - raise ValueError(f"Invalid JSON in request_body: {e}") + self.logger.error("Failed to parse request_body JSON: %s", e) + raise ValueError("Invalid JSON in request_body.") from e return r.APICall( title=component_data["title"], From e2a9da1cc8978ff249f4dc5338a1aef6dbe9bee1 Mon Sep 17 00:00:00 2001 From: Henry Webel Date: Thu, 26 Jun 2025 12:52:01 +0200 Subject: [PATCH 23/41] :art: remove trailing white-space from streamlit footer --- src/vuegen/utils/__init__.py | 2 +- .../streamlit_report/sections/Dataframes/All_Formats.py | 2 +- .../streamlit_report/sections/Home/Homepage.py | 2 +- .../streamlit_report/sections/Html/All_Html.py | 2 +- .../streamlit_report/sections/Markdown/All_Markdown.py | 2 +- .../streamlit_report/sections/Networks/Interactive_Networks.py | 2 +- .../streamlit_report/sections/Networks/Static_Networks.py | 2 +- .../streamlit_report/sections/Plots/Interactive_Plots.py | 2 +- .../streamlit_report/sections/Plots/Static_Plots.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vuegen/utils/__init__.py b/src/vuegen/utils/__init__.py index 12f5092..79eca3f 100644 --- a/src/vuegen/utils/__init__.py +++ b/src/vuegen/utils/__init__.py @@ -870,7 +870,7 @@ def generate_footer() -> str: }