Skip to content

Streamlit and quarto relative paths, and fix plots resizing #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 13, 2025
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
6 changes: 3 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ jobs:
# path: docs/_build/

# --- Streamlit example deployment ---
- name: Fix Absolute Paths in Streamlit Scripts
run: |
find docs/streamlit_report/sections -type f -name "*.py" -exec sed -i 's|/home/runner/work/vuegen/vuegen/docs/||g' {} +
#- name: Fix Absolute Paths in Streamlit Scripts
# run: |
# find docs/streamlit_report/sections -type f -name "*.py" -exec sed -i 's|/home/runner/work/vuegen/vuegen/docs/||g' {} +
- name: Publish Streamlit report to streamlit-example branch
if: startsWith(github.ref, 'refs/tags')
uses: peaceiris/actions-gh-pages@v4
Expand Down
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,5 @@ docs/images/UML_diagrams/
docs/images/Graphical_abstract/
docs/images/Nfcore_module_figure
docs/presentations/
docs/example_data/Earth_microbiome_vuegen_demo_notebook_test/
docs/vuegen_case_study_earth_microbiome_test.ipynb
test.py
basic_example_vuegen_demo_notebook_config.yaml
earth_microbiome_vuegen_demo_notebook_config.yaml
Binary file modified docs/images/vuegen_classdiagram_noattmeth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ vuegen_Chatbot_configfile

```{toctree}
:maxdepth: 2
:caption: Modules
:caption: API Reference
:hidden:

reference/vuegen
Expand Down
9 changes: 4 additions & 5 deletions docs/vuegen_basic_case_study.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,9 @@
"outputs": [],
"source": [
"# Imports\n",
"import os\n",
"import yaml\n",
"from vuegen import report_generator\n",
"from vuegen.utils import get_logger, load_yaml_config\n",
"from vuegen.utils import load_yaml_config\n",
"\n",
"if IN_COLAB:\n",
" import urllib"
Expand Down Expand Up @@ -444,7 +443,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "vuegen_py312",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand All @@ -458,9 +457,9 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.9"
"version": "3.12.6"
}
},
"nbformat": 4,
"nbformat_minor": 2
"nbformat_minor": 4
}
119 changes: 59 additions & 60 deletions src/vuegen/quarto_reportview.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import pandas as pd

from . import report as r
from .utils import create_folder, is_url, sort_imports
from .utils import create_folder, get_relative_file_path, is_url, sort_imports


class QuartoReportView(r.ReportView):
Expand Down Expand Up @@ -365,6 +365,7 @@ def _create_yaml_header(self) -> str:
r.ReportType.PDF: """
pdf:
toc: false
fig-align: center
margin:
- bottom=40mm
include-in-header:
Expand Down Expand Up @@ -568,13 +569,13 @@ def _generate_plot_content(self, plot) -> List[str]:
try:
if plot.plot_type == r.PlotType.STATIC:
plot_content.append(
self._generate_image_content(plot.file_path, width=950)
self._generate_image_content(plot.file_path, width="90%")
)
elif plot.plot_type == r.PlotType.PLOTLY:
plot_content.append(self._generate_plot_code(plot))
if self.is_report_static:
plot_content.append(
f"""fig_plotly.write_image("{static_plot_path.resolve().as_posix()}")\n```\n"""
f"""fig_plotly.write_image("{static_plot_path.relative_to("quarto_report").as_posix()}")\n```\n"""
)
plot_content.append(self._generate_image_content(static_plot_path))
else:
Expand All @@ -583,7 +584,7 @@ def _generate_plot_content(self, plot) -> List[str]:
plot_content.append(self._generate_plot_code(plot))
if self.is_report_static:
plot_content.append(
f"""fig_altair.save("{static_plot_path.resolve().as_posix()}")\n```\n"""
f"""fig_altair.save("{static_plot_path.relative_to("quarto_report").as_posix()}")\n```\n"""
)
plot_content.append(self._generate_image_content(static_plot_path))
else:
Expand Down Expand Up @@ -655,8 +656,9 @@ def _generate_plot_code(self, plot, output_file="") -> str:
response.raise_for_status()
plot_json = response.text\n"""
else: # If it's a local file
plot_rel_path = get_relative_file_path(plot.file_path, base_path="..")
plot_code += f"""
with open('{(Path("..") / plot.file_path).as_posix()}', 'r') as plot_file:
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:
Expand All @@ -669,13 +671,13 @@ def _generate_plot_code(self, plot, output_file="") -> str:
plot_json_str = json.dumps(plot_json)\n
# Create the plotly plot
fig_plotly = pio.from_json(plot_json_str)
fig_plotly.update_layout(width=950, height=500)\n"""
fig_plotly.update_layout(autosize=False, width=950, height=400, margin=dict(b=50, t=50, l=50, r=50))\n"""
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=400)\n"""
fig_altair = alt.Chart.from_json(plot_json_str).properties(width=900, height=370)\n"""
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"):
Expand Down Expand Up @@ -734,16 +736,17 @@ def _generate_dataframe_content(self, dataframe) -> List[str]:
)

# Build the file path (URL or local file)
file_path = (
dataframe.file_path
if is_url(dataframe.file_path)
else Path("..") / dataframe.file_path
)
if is_url(dataframe.file_path):
df_file_path = dataframe.file_path
else:
df_file_path = get_relative_file_path(
dataframe.file_path, base_path=".."
)

# Load the DataFrame using the correct function
read_function = read_function_mapping[file_extension]
dataframe_content.append(
f"""df = pd.{read_function.__name__}('{file_path.as_posix()}')\n"""
f"""df = pd.{read_function.__name__}('{df_file_path.as_posix()}')\n"""
)

# Display the dataframe
Expand Down Expand Up @@ -798,9 +801,10 @@ def _generate_markdown_content(self, markdown) -> List[str]:
markdown_content = response.text\n"""
)
else: # If it's a local file
md_rel_path = get_relative_file_path(markdown.file_path, base_path="..")
markdown_content.append(
f"""
with open('{(Path("..") / markdown.file_path).as_posix()}', 'r') as markdown_file:
with open('{md_rel_path.as_posix()}', 'r') as markdown_file:
markdown_content = markdown_file.read()\n"""
)

Expand All @@ -822,6 +826,39 @@ def _generate_markdown_content(self, markdown) -> List[str]:
)
return markdown_content

def _show_dataframe(self, dataframe) -> List[str]:
"""
Appends either a static image or an interactive representation of a DataFrame to the content list.

Parameters
----------
dataframe : DataFrame
The DataFrame object containing the data to display.

Returns
-------
list : List[str]
The list of content lines for the DataFrame.
"""
dataframe_content = []
if self.is_report_static:
# Generate path for the DataFrame image
df_image = (
Path(self.static_dir) / f"{dataframe.title.replace(' ', '_')}.png"
)
dataframe_content.append(
f"df.dfi.export('{Path(df_image).relative_to('quarto_report').as_posix()}', max_rows=10, max_cols=5, table_conversion='matplotlib')\n```\n"
)
# Use helper method to add centered image content
dataframe_content.append(self._generate_image_content(df_image))
else:
# Append code to display the DataFrame interactively
dataframe_content.append(
"""show(df, classes="display nowrap compact", lengthMenu=[3, 5, 10])\n```\n"""
)

return dataframe_content

def _generate_html_content(self, html) -> List[str]:
"""
Adds an HTML component to the report.
Expand All @@ -843,14 +880,13 @@ def _generate_html_content(self, html) -> List[str]:

try:
# Embed the HTML in an iframe
iframe_src = (
html.file_path
if is_url(html.file_path)
else Path("..") / html.file_path
)
if is_url(html.file_path):
html_file_path = html.file_path
else:
html_file_path = get_relative_file_path(html.file_path, base_path="..")
iframe_code = f"""
<div style="text-align: center;">
<iframe src="{iframe_src}" alt="{html.title}" width="800px" height="630px"></iframe>
<iframe src="{html_file_path.as_posix()}" alt="{html.title}" width="950px" height="530px"></iframe>
</div>\n"""
html_content.append(iframe_code)

Expand All @@ -866,7 +902,7 @@ def _generate_html_content(self, html) -> List[str]:
return html_content

def _generate_image_content(
self, image_path: str, alt_text: str = "", width: int = 650, height: int = 400
self, image_path: str, alt_text: str = "", width: str = "90%"
) -> str:
"""
Adds an image to the content list in an HTML format with a specified width and height.
Expand All @@ -889,47 +925,10 @@ def _generate_image_content(
"""
if is_url(image_path):
src = image_path
return (
f"""![]({src}){{fig-alt={alt_text} width={width} height={height}}}\n"""
)
else:
src = Path(image_path).resolve()
return (
f"""![](/{src}){{fig-alt={alt_text} width={width} height={height}}}\n"""
)

def _show_dataframe(self, dataframe) -> List[str]:
"""
Appends either a static image or an interactive representation of a DataFrame to the content list.

Parameters
----------
dataframe : DataFrame
The DataFrame object containing the data to display.
src = get_relative_file_path(image_path, base_path="..").as_posix()

Returns
-------
list : List[str]
The list of content lines for the DataFrame.
"""
dataframe_content = []
if self.is_report_static:
# Generate path for the DataFrame image
df_image = (
Path(self.static_dir) / f"{dataframe.title.replace(' ', '_')}.png"
)
dataframe_content.append(
f"df.dfi.export('{Path(df_image).resolve().as_posix()}', max_rows=10, max_cols=5, table_conversion='matplotlib')\n```\n"
)
# Use helper method to add centered image content
dataframe_content.append(self._generate_image_content(df_image))
else:
# Append code to display the DataFrame interactively
dataframe_content.append(
"""show(df, classes="display nowrap compact", lengthMenu=[3, 5, 10])\n```\n"""
)

return dataframe_content
return f"""![]({src}){{fig-alt={alt_text} width={width}}}\n"""

def _generate_component_imports(self, component: r.Component) -> List[str]:
"""
Expand Down
35 changes: 22 additions & 13 deletions src/vuegen/streamlit_reportview.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from streamlit.web import cli as stcli

from . import report as r
from .utils import create_folder, generate_footer, is_url
from .utils import create_folder, generate_footer, get_relative_file_path, is_url
from .utils.variables import make_valid_identifier


Expand Down Expand Up @@ -585,8 +585,9 @@ def _generate_plot_content(self, plot) -> List[str]:
# Add content for the different plot types
try:
if plot.plot_type == r.PlotType.STATIC:
plot_rel_path = get_relative_file_path(plot.file_path)
plot_content.append(
f"\nst.image('{plot.file_path}', caption='{plot.caption}', use_column_width=True)\n"
f"\nst.image('{plot_rel_path.as_posix()}', caption='{plot.caption}', use_column_width=True)\n"
)
elif plot.plot_type == r.PlotType.PLOTLY:
plot_content.append(self._generate_plot_code(plot))
Expand All @@ -601,7 +602,7 @@ def _generate_plot_content(self, plot) -> List[str]:
# 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()
_ = plot.create_and_save_pyvis_network(
networkx_graph, html_plot_file
)
Expand All @@ -616,13 +617,13 @@ def _generate_plot_content(self, plot) -> List[str]:
f"""
response = requests.get('{html_plot_file}')
response.raise_for_status()
html_data = response.text\n"""
html_content = response.text\n"""
)
else:
plot_content.append(
f"""
with open('{html_plot_file}', 'r') as f:
html_data = f.read()\n"""
with open('{Path(html_plot_file).relative_to(Path.cwd())}', 'r') as html_file:
html_content = html_file.read()\n"""
)

# Append the code for additional information (nodes and edges count)
Expand Down Expand Up @@ -669,8 +670,9 @@ def _generate_plot_code(self, plot) -> str:
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('{Path(plot.file_path).as_posix()}', 'r') as plot_file:
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
Expand All @@ -693,7 +695,7 @@ def _generate_plot_code(self, plot) -> str:
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_data, height=net_html_height)\n"""
st.components.v1.html(html_content, height=net_html_height)\n"""
return plot_code

def _generate_dataframe_content(self, dataframe) -> List[str]:
Expand Down Expand Up @@ -739,8 +741,14 @@ def _generate_dataframe_content(self, dataframe) -> List[str]:

# Load the DataFrame using the correct function
read_function = read_function_mapping[file_extension]

# Build the file path (URL or local file)
if is_url(dataframe.file_path):
df_file_path = dataframe.file_path
else:
df_file_path = get_relative_file_path(dataframe.file_path)
dataframe_content.append(
f"""df = pd.{read_function.__name__}('{dataframe.file_path}')\n"""
f"""df = pd.{read_function.__name__}('{df_file_path.as_posix()}')\n"""
)

# Displays a DataFrame using AgGrid with configurable options.
Expand Down Expand Up @@ -817,9 +825,10 @@ def _generate_markdown_content(self, markdown) -> List[str]:
markdown_content = response.text\n"""
)
else: # If it's a local file
md_rel_path = get_relative_file_path(markdown.file_path)
markdown_content.append(
f"""
with open('{(Path("..") / markdown.file_path).as_posix()}', 'r') as markdown_file:
with open('{md_rel_path.as_posix()}', 'r') as markdown_file:
markdown_content = markdown_file.read()\n"""
)
# Code to display md content
Expand Down Expand Up @@ -875,11 +884,11 @@ def _generate_html_content(self, html) -> List[str]:
response.raise_for_status()
html_content = response.text\n"""
)
else:
# If it's a local file
else: # If it's a local file
html_rel_path = get_relative_file_path(html.file_path)
html_content.append(
f"""
with open('{(Path("..") / html.file_path).as_posix()}', 'r', encoding='utf-8') as html_file:
with open('{html_rel_path.as_posix()}', 'r', encoding='utf-8') as html_file:
html_content = html_file.read()\n"""
)

Expand Down
Loading