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

Html representation of processor and metadata in notebooks #395

Merged
merged 9 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
29 changes: 27 additions & 2 deletions sed/core/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from typing import Any
from typing import Dict

import yaml

from sed.core.config import complete_dictionary


Expand All @@ -18,8 +20,31 @@ def __getitem__(self, val: Any) -> None:
return self._m[val]

def __repr__(self) -> str:
# TODO: #35 add pretty print, possibly to HTML
return str(self._m)
return yaml.dump(self._m, allow_unicode=True, default_flow_style=False)

def _format_attributes(self, attributes, indent=0):
html = ""
for key, value in attributes.items():
# Format key
formatted_key = key.replace("_", " ").title()
formatted_key = f"<b>{formatted_key}</b>"

if isinstance(value, dict):
html += f"<div style='padding-left: {indent * 10}px;'><details>"
html += f"<summary>{formatted_key}</summary>"
html += self._format_attributes(value, indent + 1)
html += "</details></div>"
else:
html += (
f"<div style='padding-left: {indent * 10}px;'>{formatted_key}: {value}</div>"
)
return html

def _repr_html_(self) -> str:
html = "<div>"
html += self._format_attributes(self._m)
html += "</div>"
return html

@property
def metadata(self) -> dict:
Expand Down
35 changes: 30 additions & 5 deletions sed/core/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,35 @@ def __repr__(self):
df_str = "Data Frame: No Data loaded"
else:
df_str = self._dataframe.__repr__()
attributes_str = f"Metadata: {self._attributes.metadata}"
pretty_str = df_str + "\n" + attributes_str
pretty_str = df_str + "\n" + "Metadata: " + "\n" + self._attributes.__repr__()
return pretty_str

def _repr_html_(self):
html = "<div>"

html += (
f"<details><summary>Dataframe</summary>{self.dataframe.head()._repr_html_()}</details>"
)

# Add expandable section for dataframe
html += f"<details><summary>Dask</summary>{self.dataframe._repr_html_()}</details>"

# Add expandable section for attributes
html += "<details><summary>Metadata</summary>"
html += self.attributes._repr_html_()
html += "</details>"

# Add expandable section for plots
html += "<details><summary>Plots</summary>"
# Something like the event histogram can be added here,
# but the method needs to output image/html
# self.view_event_histogram(dfpid=2, backend="matplotlib")
html += "</details>"

html += "</div>"

return html

@property
def dataframe(self) -> Union[pd.DataFrame, ddf.DataFrame]:
"""Accessor to the underlying dataframe.
Expand Down Expand Up @@ -238,13 +263,13 @@ def timed_dataframe(self, timed_dataframe: Union[pd.DataFrame, ddf.DataFrame]):
self._timed_dataframe = timed_dataframe

@property
def attributes(self) -> dict:
def attributes(self) -> MetaHandler:
"""Accessor to the metadata dict.

Returns:
dict: The metadata dict.
MetaHandler: The metadata object
"""
return self._attributes.metadata
return self._attributes

def add_attribute(self, attributes: dict, name: str, **kwds):
"""Function to add element to the attributes dict.
Expand Down
27 changes: 18 additions & 9 deletions tests/test_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,11 @@ def test_attributes_setters() -> None:
processor.dataframe["X"].compute(),
processor.dataframe["Y"].compute(),
)
processor_metadata = processor.attributes
processor_metadata = processor.attributes.metadata
assert isinstance(processor_metadata, dict)
assert "test" in processor_metadata.keys()
processor.add_attribute({"key2": 5}, name="test2")
assert processor.attributes["test2"]["key2"] == 5
assert processor_metadata["test2"]["key2"] == 5
assert processor.config["core"]["loader"] == "mpes"
assert len(processor.files) == 2

Expand Down Expand Up @@ -398,7 +398,7 @@ def test_pose_adjustment_save_load() -> None:
processor.apply_momentum_correction()
assert "Xm" in processor.dataframe.columns
assert "Ym" in processor.dataframe.columns
assert "momentum_correction" in processor.attributes
assert "momentum_correction" in processor.attributes.metadata
os.remove("sed_config_pose_adjustments.yaml")


Expand Down Expand Up @@ -609,7 +609,10 @@ def test_energy_calibration_workflow(energy_scale: str, calibration_method: str)
processor.add_energy_offset(constant=1)
processor.append_energy_axis(preview=False)
assert "energy" in processor.dataframe.columns
assert processor.attributes["energy_calibration"]["calibration"]["energy_scale"] == energy_scale
assert (
processor.attributes.metadata["energy_calibration"]["calibration"]["energy_scale"]
== energy_scale
)
os.remove(f"sed_config_energy_calibration_{energy_scale}-{calibration_method}.yaml")

energy1 = processor.dataframe["energy"].compute().values
Expand Down Expand Up @@ -743,11 +746,14 @@ def test_delay_calibration_workflow() -> None:
processor.calibrate_delay_axis()
assert "delay" in processor.dataframe.columns
assert (
processor.attributes["delay_calibration"]["calibration"]["creation_date"]
processor.attributes.metadata["delay_calibration"]["calibration"]["creation_date"]
== creation_date_calibration
)
processor.add_delay_offset(preview=True)
assert processor.attributes["delay_offset"]["offsets"]["creation_date"] == creation_date_offsets
assert (
processor.attributes.metadata["delay_offset"]["offsets"]["creation_date"]
== creation_date_offsets
)
np.testing.assert_allclose(expected, processor.dataframe["delay"].compute())
os.remove("sed_config_delay_calibration.yaml")

Expand Down Expand Up @@ -819,9 +825,12 @@ def test_add_time_stamped_data() -> None:
res = processor.dataframe["time_stamped_data"].compute().values
assert res[0] == 0
assert res[-1] == 1
assert processor.attributes["time_stamped_data"][0] == "time_stamped_data"
np.testing.assert_array_equal(processor.attributes["time_stamped_data"][1], time_stamps)
np.testing.assert_array_equal(processor.attributes["time_stamped_data"][2], data)
assert processor.attributes.metadata["time_stamped_data"][0] == "time_stamped_data"
np.testing.assert_array_equal(
processor.attributes.metadata["time_stamped_data"][1],
time_stamps,
)
np.testing.assert_array_equal(processor.attributes.metadata["time_stamped_data"][2], data)


def test_event_histogram() -> None:
Expand Down