Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/cdci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ jobs:
echo "Error: One or more protected files have been modified."
exit 1
fi
- name: streamlit report based on predefined config file
run: |
cd docs
vuegen -c example_config_files/Basic_example_vuegen_demo_notebook_config.yaml -output_dir ../tests/report_examples/Basic_example_vuegen_demo_notebook_cfg
# Check for changes
if git diff ../tests/report_examples | grep .; then
echo "Error: One or more protected files have been modified."
exit 1
fi
- name: check streamlit report files for chatbot API
run: |
vuegen -c docs/example_config_files/Chatbot_example_config.yaml -output_dir tests/report_examples/chat_bot
Expand Down Expand Up @@ -208,7 +217,7 @@ jobs:
- name: Build executable
run: |
cd gui
pyinstaller -n vuegen_gui --onefile --windowed --collect-all pyvis --collect-all streamlit --collect-all st_aggrid --collect-all customtkinter --collect-all quarto_cli --collect-all plotly --collect-all _plotly_utils --collect-all traitlets --collect-all referencing --collect-all rpds --collect-all tenacity --collect-all pandas --collect-all numpy --collect-all matplotlib --collect-all openpyxl --collect-all xlrd --collect-all nbformat --collect-all nbclient --collect-all altair --collect-all itables --collect-all kaleido --collect-all pyarrow --collect-all dataframe_image --collect-all narwhals --collect-all PIL --collect-all vl_convert --collect-all typing-extensions --add-data ../docs/example_data/Basic_example_vuegen_demo_notebook:example_data/Basic_example_vuegen_demo_notebook --add-data ../docs/images/vuegen_logo.png:. app.py
pyinstaller -n vuegen_gui --onefile --windowed --collect-all pyvis --collect-all streamlit --collect-all st_aggrid --collect-all customtkinter --collect-all quarto_cli --collect-all plotly --collect-all _plotly_utils --collect-all traitlets --collect-all referencing --collect-all rpds --collect-all tenacity --collect-all pandas --collect-all numpy --collect-all matplotlib --collect-all openpyxl --collect-all xlrd --collect-all nbformat --collect-all nbclient --collect-all altair --collect-all itables --collect-all kaleido --collect-all pyarrow --collect-all dataframe_image --collect-all narwhals --collect-all PIL --collect-all vl_convert --collect-all typing-extensions --add-data ../docs/example_data/Basic_example_vuegen_demo_notebook:example_data/Basic_example_vuegen_demo_notebook --add-data ../docs/images/logo/vuegen_logo.png:. app.py
# --windowed only for mac, see:
# https://pyinstaller.org/en/stable/usage.html#building-macos-app-bundles
# 'Under macOS, PyInstaller always builds a UNIX executable in dist.'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
report:
title: Basic Example Vuegen Demo Notebook
description: A general description of the report.
graphical_abstract: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.png
logo: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.png
graphical_abstract: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png
logo: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png
sections:
- title: Plots
description: This section contains example plots.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<!-- <div align="center">
<img width="300px" src="images/vuegen_logo.svg">
<img width="300px" src="images/logo/vuegen_logo.svg">
</div> -->

![VueGen Logo](https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.png)
![VueGen Logo](https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png)

<p align="center">
VueGen is a Python library that automates the creation of scientific reports.
Expand Down
2 changes: 1 addition & 1 deletion docs/split_readme.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# Mapping section titles to their corresponding filenames
SECTION_MAPPING = {
"![VueGen Logo](https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.svg)": "home_page.md",
"![VueGen Logo](https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/HEAD/docs/images/logo/vuegen_logo.svg)": "home_page.md",
"About the project": "about.md",
"Installation": "installation.md",
"Execution": "execution.md",
Expand Down
4 changes: 2 additions & 2 deletions docs/vuegen_basic_case_study.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@
},
"outputs": [],
"source": [
"vuegen_logo_path = \"https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.svg\"\n",
"vuegen_logo_path = \"https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png\"\n",
"\n",
"# Load the YAML file\n",
"print(\n",
Expand Down Expand Up @@ -457,7 +457,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.6"
"version": "3.12.9"
}
},
"nbformat": 4,
Expand Down
4 changes: 2 additions & 2 deletions docs/vuegen_basic_case_study_configfile.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ The [configuration file](https://github.com/Multiomics-Analytics-Group/vuegen/bl
report:
title: Basic Example Vuegen Demo Notebook
description: A general description of the report.
graphical_abstract: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.png
logo: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.png
graphical_abstract: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png
logo: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png
sections:
- title: Plots
description: This section contains example plots.
Expand Down
2 changes: 1 addition & 1 deletion gui/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ bundle:
--collect-all vl_convert \
--collect-all typing-extensions \
--add-data ../docs/example_data/Basic_example_vuegen_demo_notebook:example_data/Basic_example_vuegen_demo_notebook \
--add-data ../docs/images/vuegen_logo.png:. \
--add-data ../docs/images/logo/vuegen_logo.png:. \
app.py


Expand Down
5 changes: 3 additions & 2 deletions src/vuegen/report_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ def get_report(
report_type=report_type,
streamlit_autorun=streamlit_autorun,
static_dir=static_files_dir,
sections_dir=sections_dir,
)
st_report.generate_report(output_dir=sections_dir)
st_report.run_report(output_dir=sections_dir)
st_report.generate_report()
st_report.run_report()
else:
# Check if Quarto is installed
if shutil.which("quarto") is None and not hasattr(
Expand Down
112 changes: 90 additions & 22 deletions src/vuegen/streamlit_reportview.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def __init__(
report_type: r.ReportType,
streamlit_autorun: bool = False,
static_dir: str = STATIC_FILES_DIR,
sections_dir: str = SECTIONS_DIR,
):
"""Initialize ReportView with the report and report type.

Expand Down Expand Up @@ -86,8 +87,9 @@ def __init__(
}

self.static_dir = static_dir
self.section_dir = sections_dir

def generate_report(self, output_dir: str = SECTIONS_DIR) -> None:
def generate_report(self, output_dir: str = None) -> None:
"""
Generates the Streamlit report and creates Python files for each section
and its subsections and plots.
Expand All @@ -98,6 +100,10 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None:
The folder where the generated report files will be saved
(default is SECTIONS_DIR).
"""
if output_dir is not None:
# ? does this imply changes to the static dir
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Should the static dir always be in the output folder?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think so, if there are interactive components when creating static reports, we save these plots in that folder, and read from it.

self.section_dir = Path(output_dir).resolve()
output_dir = Path(self.section_dir)
self.report.logger.debug(
"Generating '%s' report in directory: '%s'", self.report_type, output_dir
)
Expand Down Expand Up @@ -274,6 +280,29 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None:

# Create Python files for each section and its subsections and plots
self._generate_sections(output_dir=output_dir)

# Save README.md to the output directory
fpath = self.section_dir.parent / "README.md"
with open(fpath, "w", encoding="utf-8") as f:

f.write(
textwrap.dedent(
f"""\
# Streamlit Report

This report was generated using the Vuegen library:
https://github.com/Multiomics-Analytics-Group/vuegen

Executed from: `{Path.cwd()}`

Written to: `{self.section_dir.resolve()}`

Folder cannot be moved from above path, but can be executed
from anywhere on the system.
"""
)
)

except Exception as e:
self.report.logger.error(
"An error occurred while generating the report: %s",
Expand All @@ -282,7 +311,7 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None:
)
raise

def run_report(self, output_dir: str = SECTIONS_DIR) -> None:
def run_report(self, output_dir: str = None) -> None:
"""
Runs the generated Streamlit report.

Expand All @@ -291,6 +320,9 @@ def run_report(self, output_dir: str = SECTIONS_DIR) -> None:
output_dir : str, optional
The folder where the report was generated (default is SECTIONS_DIR).
"""
if output_dir is not None:
self.report.logger.warning("The output_dir parameter is deprecated.")
output_dir = Path(self.section_dir)
if self.streamlit_autorun:
self.report.logger.info(
"Running '%s' %s report.", self.report.title, self.report_type
Expand Down Expand Up @@ -657,10 +689,17 @@ def _generate_plot_content(self, plot) -> List[str]:
# If the file path is a URL, keep the file path as is
if is_url(plot.file_path):
plot_file_path = plot.file_path
plot_content.append(f"plot_file_path = '{plot_file_path}'")
else: # If it's a local file
plot_file_path = get_relative_file_path(plot.file_path).as_posix()
plot_file_path = get_relative_file_path(
plot.file_path, relative_to=self.section_dir
).as_posix()
plot_content.append(
f"plot_file_path = (section_dir / '{plot_file_path}')"
".resolve().as_posix()"
)
plot_content.append(
f"\nst.image('{plot_file_path}', "
"st.image(plot_file_path,"
f" caption='{plot.caption}', use_column_width=True)\n"
)
elif plot.plot_type == r.PlotType.PLOTLY:
Expand Down Expand Up @@ -699,11 +738,14 @@ def _generate_plot_content(self, plot) -> List[str]:
)
)
else:
fpath = Path(html_plot_file).resolve().relative_to(Path.cwd())
fpath = get_relative_file_path(
html_plot_file, relative_to=self.section_dir
).as_posix()
plot_content.append(
textwrap.dedent(
f"""
with open('{fpath}', 'r') as html_file:
file_path = (section_dir / '{fpath}').resolve().as_posix()
with open(file_path, 'r') as html_file:
html_content = html_file.read()
"""
)
Expand Down Expand Up @@ -770,10 +812,13 @@ def _generate_plot_code(self, plot) -> str:
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_rel_path = get_relative_file_path(
plot.file_path, relative_to=self.section_dir
).as_posix()
plot_code = textwrap.dedent(
f"""
with open('{plot_rel_path.as_posix()}', 'r') as plot_file:
file_path = (section_dir / '{plot_rel_path}').resolve().as_posix()
with open(file_path, 'r') as plot_file:
plot_json = json.load(plot_file)\n"""
)

Expand Down Expand Up @@ -864,11 +909,14 @@ def _generate_dataframe_content(self, dataframe) -> List[str]:
sheet_names = table_utils.get_sheet_names(df_file_path.as_posix())
if len(sheet_names) > 1:
# If there are multiple sheets, ask the user to select one
fpath = df_file_path.as_posix()
fpath = get_relative_file_path(
dataframe.file_path, relative_to=self.section_dir
).as_posix()
dataframe_content.append(
textwrap.dedent(
f"""\
sheet_names = table_utils.get_sheet_names("{fpath}")
file_path = (section_dir / '{fpath}').resolve().as_posix()
sheet_names = table_utils.get_sheet_names(file_path)
selected_sheet = st.selectbox("Select a sheet to display",
options=sheet_names,
)
Expand All @@ -877,18 +925,27 @@ def _generate_dataframe_content(self, dataframe) -> List[str]:
)

# Load the DataFrame using the correct function
read_function = read_function_mapping[file_extension]
df_file_path = get_relative_file_path(
dataframe.file_path, relative_to=self.section_dir
).as_posix()
read_function = read_function_mapping[file_extension].__name__
if file_extension in [
r.DataFrameFormat.XLS.value_with_dot,
r.DataFrameFormat.XLSX.value_with_dot,
]:
dataframe_content.append(
f"df = pd.{read_function.__name__}('{df_file_path.as_posix()}',"
" sheet_name=selected_sheet)\n"
textwrap.dedent(
f"""\
file_path = (section_dir / '{df_file_path}').resolve()
df = pd.{read_function}(file_path, sheet_name=selected_sheet)
"""
)
)
else:
dataframe_content.append(
f"df = pd.{read_function.__name__}('{df_file_path.as_posix()}')\n"
f"file_path = (section_dir / '{df_file_path}'"
").resolve().as_posix()\n"
f"df = pd.{read_function}(file_path)\n"
)
# ! Alternative to select box: iterate over sheets in DataFrame
# Displays a DataFrame using AgGrid with configurable options.
Expand Down Expand Up @@ -982,11 +1039,15 @@ def _generate_markdown_content(self, markdown) -> List[str]:
)
)
else: # If it's a local file
md_rel_path = get_relative_file_path(markdown.file_path)
md_rel_path = get_relative_file_path(
markdown.file_path, relative_to=self.section_dir
).as_posix()

markdown_content.append(
textwrap.dedent(
f"""
with open('{md_rel_path.as_posix()}', 'r') as markdown_file:
file_path = (section_dir / '{md_rel_path}').resolve().as_posix()
with open(file_path, 'r') as markdown_file:
markdown_content = markdown_file.read()
"""
)
Expand Down Expand Up @@ -1052,13 +1113,16 @@ def _generate_html_content(self, html) -> List[str]:
)
)
else: # If it's a local file
html_rel_path = get_relative_file_path(html.file_path).as_posix()
html_rel_path = get_relative_file_path(
html.file_path, relative_to=self.section_dir
).as_posix()
html_content.append(
textwrap.dedent(
f"""
with open('{html_rel_path}', 'r', encoding='utf-8') as f:
html_content = f.read()
"""
f"""\
file_path = (section_dir / '{html_rel_path}').resolve().as_posix()
with open(file_path, 'r', encoding='utf-8') as f:
html_content = f.read()
"""
)
)

Expand Down Expand Up @@ -1376,7 +1440,11 @@ def _generate_component_imports(self, component: r.Component) -> List[str]:
}

component_type = component.component_type
component_imports = ["import streamlit as st"]
component_imports = [
"import streamlit as st",
"from pathlib import Path",
"section_dir = Path(__file__).resolve().parent.parent",
]

# Add relevant imports based on component type and visualization tool
if component_type == r.ComponentType.PLOT:
Expand Down
Loading