Skip to content
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

Replace haddock3-analysis-components with haddock3-ui package #1025

Merged
merged 15 commits into from
Oct 4, 2024
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
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ recursive-include varia *.inc
recursive-include varia *.lua
recursive-include varia *.md
include src/haddock/bin/*
include src/haddock/libs/assets/*
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,3 @@ include-package-data = true

[tool.setuptools.package-data]
haddock = ["bin/*"]

2 changes: 1 addition & 1 deletion src/haddock/clis/cli_analyse.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ def analyse_step(
offline=offline,
)
tables = clt_table_handler(clt_file, ss_file, is_cleaned)
report_generator(boxes, scatters, tables, step)
report_generator(boxes, scatters, tables, step, '.', offline)
# provide a zipped archive of the top ranked structures
zip_top_ranked(ss_file, cluster_ranking, Path("summary.tgz"))

Expand Down
12 changes: 12 additions & 0 deletions src/haddock/libs/assets/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""Static assets

Static assets for analysis report.

Files where downloaded from
https://cdn.jsdelivr.net/npm/@i-vresse/haddock3-ui@~0.3.0/dist/index.css
and
https://cdn.jsdelivr.net/npm/@i-vresse/haddock3-ui@~0.3.0/dist/report.bundle.js
"""
import importlib.resources as importlib_resources

haddock_ui_path = importlib_resources.files("haddock.libs.assets")
1 change: 1 addition & 0 deletions src/haddock/libs/assets/index.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5,263 changes: 5,263 additions & 0 deletions src/haddock/libs/assets/report.bundle.js

Large diffs are not rendered by default.

92 changes: 53 additions & 39 deletions src/haddock/libs/libplots.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Plotting functionalities."""

import json
import shutil

import numpy as np
import pandas as pd
Expand All @@ -24,6 +25,7 @@
Optional,
Union,
)
from haddock.libs.assets import haddock_ui_path


SCATTER_PAIRS = [
Expand Down Expand Up @@ -940,10 +942,15 @@ def clt_table_handler(clt_file, ss_file, is_cleaned=False):
return df_merged


def _css_styles_for_report():
def _css_styles_for_report(offline: bool) -> str:
"""
Generate custom CSS styles for an analysis report.

Parameters
----------
offline : bool
If True, the HTML will be generated for offline use.

Returns
-------
The CSS styles as a string.
Expand All @@ -953,39 +960,44 @@ def _css_styles_for_report():
font-family: Arial, sans-serif;
font-size: 32px;
font-weight: bold;
}

}
body {
margin-left: 1em;
}
table {
border-collapse: collapse;
}

th {
background-color: #f2f2f2;
padding: 8px;
border: 1px solid #ddd;
text-align: left;
}

th[scope="row"] {
position: sticky;
min-width: 16rem;
left: 0;
z-index: 1
}

td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}

tr:nth-child(even) {
background-color: #f2f2f2
}
VGPReys marked this conversation as resolved.
Show resolved Hide resolved

.js-plotly-plot .plotly .modebar svg {
display: inline;
}
"""
css_link = "https://esm.sh/@i-vresse/haddock3-analysis-components@~0.4.1/dist/style.css" # noqa:E501
table_css = f' <link href={css_link} rel="stylesheet" />'
css_link = "https://cdn.jsdelivr.net/npm/@i-vresse/haddock3-ui@~0.3.0/dist/index.css"
if offline:
# copy the css file to the report directory
src = haddock_ui_path / 'index.css'
shutil.copyfile(str(src), "../data/ui/index.css")
css_link = "../../data/ui/index.css"
table_css = f' <link href="{css_link}" rel="stylesheet" />'
return f"{table_css}<style>{custom_css}</style>"


Expand All @@ -1006,27 +1018,31 @@ def _generate_html_report(
A list of figures to include in the HTML body.
Each figure can be either a string representing a table or a
plotly.graph_objects.Figure object.
offline : bool
If True, the HTML will be generated for offline use.

Returns
-------
html_report : str
The generated HTML report as a string.
"""
html_report = "<!DOCTYPE html><html lang='en'>"
html_report += _generate_html_head(step)
html_report += _generate_html_head(step, offline)
html_report += _generate_html_body(figures, report_path, offline=offline)
html_report += "</html>"
return html_report


def _generate_html_head(step):
def _generate_html_head(step, offline):
"""
Generate the HTML head section for an analysis report.

Parameters
----------
step : str
The step number.
offline : bool
If True, the HTML will be generated for offline use.

Returns
-------
Expand All @@ -1036,24 +1052,15 @@ def _generate_html_head(step):
head = "<head>"
head += f"<title>Analysis report of step {step}</title>"
head += f"<p class='title'>Analysis report of step {step}</p>"
head += _css_styles_for_report()
head += """
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@^18.2.0",
"react-dom": "https://esm.sh/react-dom@^18.2.0",
"@i-vresse/haddock3-analysis-components": "https://esm.sh/@i-vresse/haddock3-analysis-components@~0.4.1?bundle"
}
}
</script>""" # noqa : E501
head += _css_styles_for_report(offline)
head += "</head>"
return head


def _generate_unclustered_table_html(
table_id: str,
df: pd.DataFrame,
bundle_url: str,
) -> str:
data = df.to_json(orient='records')
headers = [
Expand All @@ -1077,21 +1084,18 @@ def _generate_unclustered_table_html(
}}
</script>
<script type="module">
import {{createRoot}} from "react-dom"
import {{createElement}} from "react"
import {{StructureTable}} from "@i-vresse/haddock3-analysis-components"
import {{ renderStructureTable }} from "{bundle_url}";

const props = JSON.parse(document.getElementById("data{table_id}").text)

createRoot(document.getElementById('{table_id}')).render(
createElement(StructureTable, props)
)
renderStructureTable(document.getElementById('{table_id}'), props.headers, props.structures)
</script>""" # noqa : E501


def _generate_clustered_table_html(
table_id: str,
df: pd.DataFrame,
bundle_url: str,
) -> str:
data = df.to_json(orient='records')
nr_best_columns = df.filter(like="best").shape[1]
Expand Down Expand Up @@ -1127,15 +1131,10 @@ def _generate_clustered_table_html(
}}
</script>
<script type="module">
import {{createRoot}} from "react-dom"
import {{createElement}} from "react"
import {{ClusterTable}} from "@i-vresse/haddock3-analysis-components"

import {{ renderClusterTable }} from "{bundle_url}";
const props = JSON.parse(document.getElementById("data{table_id}").text)

createRoot(document.getElementById('{table_id}')).render(
createElement(ClusterTable, props)
)
renderClusterTable(document.getElementById('{table_id}'), props.headers, props.clusters);
</script>""" # noqa : E501


Expand All @@ -1153,6 +1152,8 @@ def _generate_html_body(
A list of figures to include in the HTML body.
Each figure can be either a string representing a table or a
plotly.graph_objects.Figure object.
offline : bool
If True, the HTML will be generated for offline use.

Returns
-------
Expand All @@ -1168,10 +1169,16 @@ def _generate_html_body(
table_id = f"table{table_index}"

is_unclustered = 'cluster_rank' not in figure
bundle_url = "https://cdn.jsdelivr.net/npm/@i-vresse/haddock3-ui@~0.3.0/dist/report.bundle.js"
if offline:
# copy the bundle to the run_dir folder
src = haddock_ui_path / 'report.bundle.js'
shutil.copyfile(str(src), "../data/ui/report.bundle.js")
bundle_url = "../../data/ui/report.bundle.js"
if is_unclustered:
inner_html = _generate_unclustered_table_html(table_id, figure)
inner_html = _generate_unclustered_table_html(table_id, figure, bundle_url)
else:
inner_html = _generate_clustered_table_html(table_id, figure)
inner_html = _generate_clustered_table_html(table_id, figure, bundle_url)
else: # plots
inner_json = figure.to_json()
inner_html = create_html(
Expand All @@ -1194,6 +1201,7 @@ def report_generator(
tables: list,
step: str,
directory: FilePath = ".",
offline: bool = False
) -> None:
"""
Create a figure include plots and tables.
Expand All @@ -1209,6 +1217,10 @@ def report_generator(
list of scatter plots generated by scatter_plot_handler
table: list
a list including tables generated by clt_table_handler
directory : Path
path to the output folder
offline: bool
If True, the HTML will be generated for offline use.
"""
figures = [tables]
# Combine scatters
Expand All @@ -1222,9 +1234,11 @@ def report_generator(
# Combine boxes"
figures.append(report_plots_handler(boxes))

if offline:
Path('../data/ui').mkdir(parents=True, exist_ok=True)
# Write everything to a html file
report_path = Path(directory, "report.html")
html_report = _generate_html_report(step, figures, report_path)
html_report = _generate_html_report(step, figures, report_path, offline)
with open(report_path, "w", encoding="utf-8") as report:
report.write(html_report)

Expand Down
Loading
Loading