Skip to content

Commit 8a58b5e

Browse files
authored
Merge branch 'main' into fix150_multiline_description
2 parents 5ac335c + 1452c24 commit 8a58b5e

File tree

27 files changed

+1160
-70
lines changed

27 files changed

+1160
-70
lines changed

.github/workflows/cdci.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ jobs:
118118
echo "Error: One or more protected files have been modified."
119119
exit 1
120120
fi
121+
- name: streamlit report based on predefined config file
122+
run: |
123+
cd docs
124+
vuegen -c example_config_files/Basic_example_vuegen_demo_notebook_config.yaml -output_dir ../tests/report_examples/Basic_example_vuegen_demo_notebook_cfg
125+
# Check for changes
126+
if git diff ../tests/report_examples | grep .; then
127+
echo "Error: One or more protected files have been modified."
128+
exit 1
129+
fi
121130
- name: check streamlit report files for chatbot API
122131
run: |
123132
vuegen -c docs/example_config_files/Chatbot_example_config.yaml -output_dir tests/report_examples/chat_bot
@@ -208,7 +217,7 @@ jobs:
208217
- name: Build executable
209218
run: |
210219
cd gui
211-
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
220+
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
212221
# --windowed only for mac, see:
213222
# https://pyinstaller.org/en/stable/usage.html#building-macos-app-bundles
214223
# 'Under macOS, PyInstaller always builds a UNIX executable in dist.'

docs/example_config_files/Basic_example_vuegen_demo_notebook_config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
report:
22
title: Basic Example Vuegen Demo Notebook
33
description: A general description of the report.
4-
graphical_abstract: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.png
5-
logo: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.png
4+
graphical_abstract: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png
5+
logo: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png
66
sections:
77
- title: Plots
88
description: This section contains example plots.

docs/example_data/Basic_example_vuegen_demo_notebook/5_Markdown/1_All_markdown/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<!-- <div align="center">
2-
<img width="300px" src="images/vuegen_logo.svg">
2+
<img width="300px" src="images/logo/vuegen_logo.svg">
33
</div> -->
44

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

77
<p align="center">
88
VueGen is a Python library that automates the creation of scientific reports.

docs/split_readme.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
# Mapping section titles to their corresponding filenames
55
SECTION_MAPPING = {
6-
"![VueGen Logo](https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.svg)": "home_page.md",
6+
"![VueGen Logo](https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/HEAD/docs/images/logo/vuegen_logo.svg)": "home_page.md",
77
"About the project": "about.md",
88
"Installation": "installation.md",
99
"Execution": "execution.md",
@@ -84,7 +84,7 @@ def clean_trailing_links(content):
8484

8585

8686
def process_readme(readme_path, output_dir):
87-
readme = Path(readme_path).read_text()
87+
readme = Path(readme_path).read_text(encoding="utf-8")
8888

8989
# Extract links from README
9090
links = extract_links_from_readme(readme)
@@ -104,11 +104,11 @@ def process_readme(readme_path, output_dir):
104104
(output_dir / filename).write_text(myst_content)
105105
print(f"Generated {filename}")
106106
else:
107-
print(f"Warning: Section '{section_title}' not found in README")
107+
raise ValueError(f"Section '{section_title}' not found in README")
108108

109109
# Include CONTRIBUTING.md with its own link references
110110
contrib_path = readme_path.parent / "CONTRIBUTING.md"
111-
if contrib_path.exists():
111+
try:
112112
raw_contrib = contrib_path.read_text()
113113
contrib_links = extract_links_from_readme(raw_contrib)
114114

@@ -121,8 +121,8 @@ def process_readme(readme_path, output_dir):
121121
# Write output
122122
(output_dir / "contributing.md").write_text(contrib_converted)
123123
print("Generated contributing.md")
124-
else:
125-
print("Warning: CONTRIBUTING.md not found")
124+
except FileNotFoundError as e:
125+
raise FileNotFoundError(f"CONTRIBUTING.md not found at {contrib_path}") from e
126126

127127

128128
if __name__ == "__main__":

docs/vuegen_basic_case_study.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@
273273
},
274274
"outputs": [],
275275
"source": [
276-
"vuegen_logo_path = \"https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.svg\"\n",
276+
"vuegen_logo_path = \"https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png\"\n",
277277
"\n",
278278
"# Load the YAML file\n",
279279
"print(\n",
@@ -457,7 +457,7 @@
457457
"name": "python",
458458
"nbconvert_exporter": "python",
459459
"pygments_lexer": "ipython3",
460-
"version": "3.12.6"
460+
"version": "3.12.9"
461461
}
462462
},
463463
"nbformat": 4,

docs/vuegen_basic_case_study_configfile.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ The [configuration file](https://github.com/Multiomics-Analytics-Group/vuegen/bl
66
report:
77
title: Basic Example Vuegen Demo Notebook
88
description: A general description of the report.
9-
graphical_abstract: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.png
10-
logo: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_logo.png
9+
graphical_abstract: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png
10+
logo: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/logo/vuegen_logo.png
1111
sections:
1212
- title: Plots
1313
description: This section contains example plots.

gui/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ bundle:
3939
--collect-all vl_convert \
4040
--collect-all typing-extensions \
4141
--add-data ../docs/example_data/Basic_example_vuegen_demo_notebook:example_data/Basic_example_vuegen_demo_notebook \
42-
--add-data ../docs/images/vuegen_logo.png:. \
42+
--add-data ../docs/images/logo/vuegen_logo.png:. \
4343
app.py
4444

4545

src/vuegen/report_generator.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,10 @@ def get_report(
108108
report_type=report_type,
109109
streamlit_autorun=streamlit_autorun,
110110
static_dir=static_files_dir,
111+
sections_dir=sections_dir,
111112
)
112-
st_report.generate_report(output_dir=sections_dir)
113-
st_report.run_report(output_dir=sections_dir)
113+
st_report.generate_report()
114+
st_report.run_report()
114115
else:
115116
# Check if Quarto is installed
116117
if shutil.which("quarto") is None and not hasattr(

src/vuegen/streamlit_reportview.py

Lines changed: 90 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def __init__(
5050
report_type: r.ReportType,
5151
streamlit_autorun: bool = False,
5252
static_dir: str = STATIC_FILES_DIR,
53+
sections_dir: str = SECTIONS_DIR,
5354
):
5455
"""Initialize ReportView with the report and report type.
5556
@@ -86,8 +87,9 @@ def __init__(
8687
}
8788

8889
self.static_dir = static_dir
90+
self.section_dir = sections_dir
8991

90-
def generate_report(self, output_dir: str = SECTIONS_DIR) -> None:
92+
def generate_report(self, output_dir: str = None) -> None:
9193
"""
9294
Generates the Streamlit report and creates Python files for each section
9395
and its subsections and plots.
@@ -98,6 +100,10 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None:
98100
The folder where the generated report files will be saved
99101
(default is SECTIONS_DIR).
100102
"""
103+
if output_dir is not None:
104+
# ? does this imply changes to the static dir
105+
self.section_dir = Path(output_dir).resolve()
106+
output_dir = Path(self.section_dir)
101107
self.report.logger.debug(
102108
"Generating '%s' report in directory: '%s'", self.report_type, output_dir
103109
)
@@ -274,6 +280,29 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None:
274280

275281
# Create Python files for each section and its subsections and plots
276282
self._generate_sections(output_dir=output_dir)
283+
284+
# Save README.md to the output directory
285+
fpath = self.section_dir.parent / "README.md"
286+
with open(fpath, "w", encoding="utf-8") as f:
287+
288+
f.write(
289+
textwrap.dedent(
290+
f"""\
291+
# Streamlit Report
292+
293+
This report was generated using the Vuegen library:
294+
https://github.com/Multiomics-Analytics-Group/vuegen
295+
296+
Executed from: `{Path.cwd()}`
297+
298+
Written to: `{self.section_dir.resolve()}`
299+
300+
Folder cannot be moved from above path, but can be executed
301+
from anywhere on the system.
302+
"""
303+
)
304+
)
305+
277306
except Exception as e:
278307
self.report.logger.error(
279308
"An error occurred while generating the report: %s",
@@ -282,7 +311,7 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None:
282311
)
283312
raise
284313

285-
def run_report(self, output_dir: str = SECTIONS_DIR) -> None:
314+
def run_report(self, output_dir: str = None) -> None:
286315
"""
287316
Runs the generated Streamlit report.
288317
@@ -291,6 +320,9 @@ def run_report(self, output_dir: str = SECTIONS_DIR) -> None:
291320
output_dir : str, optional
292321
The folder where the report was generated (default is SECTIONS_DIR).
293322
"""
323+
if output_dir is not None:
324+
self.report.logger.warning("The output_dir parameter is deprecated.")
325+
output_dir = Path(self.section_dir)
294326
if self.streamlit_autorun:
295327
self.report.logger.info(
296328
"Running '%s' %s report.", self.report.title, self.report_type
@@ -659,10 +691,17 @@ def _generate_plot_content(self, plot) -> List[str]:
659691
# If the file path is a URL, keep the file path as is
660692
if is_url(plot.file_path):
661693
plot_file_path = plot.file_path
694+
plot_content.append(f"plot_file_path = '{plot_file_path}'")
662695
else: # If it's a local file
663-
plot_file_path = get_relative_file_path(plot.file_path).as_posix()
696+
plot_file_path = get_relative_file_path(
697+
plot.file_path, relative_to=self.section_dir
698+
).as_posix()
699+
plot_content.append(
700+
f"plot_file_path = (section_dir / '{plot_file_path}')"
701+
".resolve().as_posix()"
702+
)
664703
plot_content.append(
665-
f"\nst.image('{plot_file_path}', "
704+
"st.image(plot_file_path,"
666705
f" caption='{plot.caption}', use_column_width=True)\n"
667706
)
668707
elif plot.plot_type == r.PlotType.PLOTLY:
@@ -701,11 +740,14 @@ def _generate_plot_content(self, plot) -> List[str]:
701740
)
702741
)
703742
else:
704-
fpath = Path(html_plot_file).resolve().relative_to(Path.cwd())
743+
fpath = get_relative_file_path(
744+
html_plot_file, relative_to=self.section_dir
745+
).as_posix()
705746
plot_content.append(
706747
textwrap.dedent(
707748
f"""
708-
with open('{fpath}', 'r') as html_file:
749+
file_path = (section_dir / '{fpath}').resolve().as_posix()
750+
with open(file_path, 'r') as html_file:
709751
html_content = html_file.read()
710752
"""
711753
)
@@ -772,10 +814,13 @@ def _generate_plot_code(self, plot) -> str:
772814
plot_json = json.loads(response.text)\n"""
773815
)
774816
else: # If it's a local file
775-
plot_rel_path = get_relative_file_path(plot.file_path)
817+
plot_rel_path = get_relative_file_path(
818+
plot.file_path, relative_to=self.section_dir
819+
).as_posix()
776820
plot_code = textwrap.dedent(
777821
f"""
778-
with open('{plot_rel_path.as_posix()}', 'r') as plot_file:
822+
file_path = (section_dir / '{plot_rel_path}').resolve().as_posix()
823+
with open(file_path, 'r') as plot_file:
779824
plot_json = json.load(plot_file)\n"""
780825
)
781826

@@ -866,11 +911,14 @@ def _generate_dataframe_content(self, dataframe) -> List[str]:
866911
sheet_names = table_utils.get_sheet_names(df_file_path.as_posix())
867912
if len(sheet_names) > 1:
868913
# If there are multiple sheets, ask the user to select one
869-
fpath = df_file_path.as_posix()
914+
fpath = get_relative_file_path(
915+
dataframe.file_path, relative_to=self.section_dir
916+
).as_posix()
870917
dataframe_content.append(
871918
textwrap.dedent(
872919
f"""\
873-
sheet_names = table_utils.get_sheet_names("{fpath}")
920+
file_path = (section_dir / '{fpath}').resolve().as_posix()
921+
sheet_names = table_utils.get_sheet_names(file_path)
874922
selected_sheet = st.selectbox("Select a sheet to display",
875923
options=sheet_names,
876924
)
@@ -879,18 +927,27 @@ def _generate_dataframe_content(self, dataframe) -> List[str]:
879927
)
880928

881929
# Load the DataFrame using the correct function
882-
read_function = read_function_mapping[file_extension]
930+
df_file_path = get_relative_file_path(
931+
dataframe.file_path, relative_to=self.section_dir
932+
).as_posix()
933+
read_function = read_function_mapping[file_extension].__name__
883934
if file_extension in [
884935
r.DataFrameFormat.XLS.value_with_dot,
885936
r.DataFrameFormat.XLSX.value_with_dot,
886937
]:
887938
dataframe_content.append(
888-
f"df = pd.{read_function.__name__}('{df_file_path.as_posix()}',"
889-
" sheet_name=selected_sheet)\n"
939+
textwrap.dedent(
940+
f"""\
941+
file_path = (section_dir / '{df_file_path}').resolve()
942+
df = pd.{read_function}(file_path, sheet_name=selected_sheet)
943+
"""
944+
)
890945
)
891946
else:
892947
dataframe_content.append(
893-
f"df = pd.{read_function.__name__}('{df_file_path.as_posix()}')\n"
948+
f"file_path = (section_dir / '{df_file_path}'"
949+
").resolve().as_posix()\n"
950+
f"df = pd.{read_function}(file_path)\n"
894951
)
895952
# ! Alternative to select box: iterate over sheets in DataFrame
896953
# Displays a DataFrame using AgGrid with configurable options.
@@ -984,11 +1041,15 @@ def _generate_markdown_content(self, markdown) -> List[str]:
9841041
)
9851042
)
9861043
else: # If it's a local file
987-
md_rel_path = get_relative_file_path(markdown.file_path)
1044+
md_rel_path = get_relative_file_path(
1045+
markdown.file_path, relative_to=self.section_dir
1046+
).as_posix()
1047+
9881048
markdown_content.append(
9891049
textwrap.dedent(
9901050
f"""
991-
with open('{md_rel_path.as_posix()}', 'r') as markdown_file:
1051+
file_path = (section_dir / '{md_rel_path}').resolve().as_posix()
1052+
with open(file_path, 'r') as markdown_file:
9921053
markdown_content = markdown_file.read()
9931054
"""
9941055
)
@@ -1054,13 +1115,16 @@ def _generate_html_content(self, html) -> List[str]:
10541115
)
10551116
)
10561117
else: # If it's a local file
1057-
html_rel_path = get_relative_file_path(html.file_path).as_posix()
1118+
html_rel_path = get_relative_file_path(
1119+
html.file_path, relative_to=self.section_dir
1120+
).as_posix()
10581121
html_content.append(
10591122
textwrap.dedent(
1060-
f"""
1061-
with open('{html_rel_path}', 'r', encoding='utf-8') as f:
1062-
html_content = f.read()
1063-
"""
1123+
f"""\
1124+
file_path = (section_dir / '{html_rel_path}').resolve().as_posix()
1125+
with open(file_path, 'r', encoding='utf-8') as f:
1126+
html_content = f.read()
1127+
"""
10641128
)
10651129
)
10661130

@@ -1378,7 +1442,11 @@ def _generate_component_imports(self, component: r.Component) -> List[str]:
13781442
}
13791443

13801444
component_type = component.component_type
1381-
component_imports = ["import streamlit as st"]
1445+
component_imports = [
1446+
"import streamlit as st",
1447+
"from pathlib import Path",
1448+
"section_dir = Path(__file__).resolve().parent.parent",
1449+
]
13821450

13831451
# Add relevant imports based on component type and visualization tool
13841452
if component_type == r.ComponentType.PLOT:

0 commit comments

Comments
 (0)