Skip to content

Commit

Permalink
chore: Serialization of items is now only in UI module (#1282)
Browse files Browse the repository at this point in the history
As a preparation work to serialize reporters and to limit the footprint
of UI to only it's module, this PR moves every serialization related
code to the UI module.
  • Loading branch information
rouk1 authored Feb 4, 2025
1 parent 80d3504 commit 7668219
Show file tree
Hide file tree
Showing 31 changed files with 432 additions and 351 deletions.
11 changes: 0 additions & 11 deletions skore/src/skore/persistence/item/altair_chart_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from skore.persistence.item.item import Item, ItemTypeError
from skore.persistence.item.media_item import lazy_is_instance
from skore.utils import bytes_to_b64_str

if TYPE_CHECKING:
from altair.vegalite.v5.schema.core import TopLevelSpec as AltairChart
Expand Down Expand Up @@ -69,13 +68,3 @@ def chart(self) -> AltairChart:
import altair

return altair.Chart.from_json(self.chart_str)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
chart_bytes = self.chart_str.encode("utf-8")
chart_b64_str = bytes_to_b64_str(chart_bytes)

return super().as_serializable_dict() | {
"media_type": "application/vnd.vega.v5+json;base64",
"value": chart_b64_str,
}
12 changes: 0 additions & 12 deletions skore/src/skore/persistence/item/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,3 @@ def __parameters__(self) -> dict[str, Any]:
def __repr__(self) -> str:
"""Represent the item."""
return f"{self.__class__.__name__}(...)"

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend.
Derived class must call their super implementation and merge the result with
their output.
"""
return {
"updated_at": self.updated_at,
"created_at": self.created_at,
"note": self.note,
}
13 changes: 0 additions & 13 deletions skore/src/skore/persistence/item/matplotlib_figure_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,3 @@ def figure(self) -> Figure:
mpl_backend(backend="agg"),
):
return joblib.load(stream)

def as_serializable_dict(self) -> dict:
"""Convert item to a JSON-serializable dict to used by frontend."""
with BytesIO() as stream:
self.figure.savefig(stream, format="svg", bbox_inches="tight")

figure_bytes = stream.getvalue()
figure_b64_str = bytes_to_b64_str(figure_bytes)

return super().as_serializable_dict() | {
"media_type": "image/svg+xml;base64",
"value": figure_b64_str,
}
7 changes: 0 additions & 7 deletions skore/src/skore/persistence/item/media_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,3 @@ def factory(
raise ValueError(f"MIME type '{media_type}' is not supported.")

return cls(media, media_type, **kwargs)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
return super().as_serializable_dict() | {
"media_type": self.media_type,
"value": self.media,
}
7 changes: 0 additions & 7 deletions skore/src/skore/persistence/item/numpy_array_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,3 @@ def factory(cls, array: numpy.ndarray, /, **kwargs) -> NumpyArrayItem:
array_bytes = stream.getvalue()
array_b64_str = bytes_to_b64_str(array_bytes)
return cls(array_b64_str=array_b64_str, **kwargs)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
return super().as_serializable_dict() | {
"media_type": "text/markdown",
"value": self.array.tolist(),
}
7 changes: 0 additions & 7 deletions skore/src/skore/persistence/item/pandas_dataframe_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,3 @@ def factory(cls, dataframe: pandas.DataFrame, /, **kwargs) -> PandasDataFrameIte
dataframe_json=dataframe.to_json(orient=PandasDataFrameItem.ORIENT),
**kwargs,
)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
return super().as_serializable_dict() | {
"media_type": "application/vnd.dataframe",
"value": self.dataframe.fillna("NaN").to_dict(orient="tight"),
}
7 changes: 0 additions & 7 deletions skore/src/skore/persistence/item/pandas_series_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,3 @@ def factory(cls, series: pandas.Series, /, **kwargs) -> PandasSeriesItem:
series_json=series.to_json(orient=PandasSeriesItem.ORIENT),
**kwargs,
)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
return super().as_serializable_dict() | {
"value": self.series.fillna("NaN").to_list(),
"media_type": "text/markdown",
}
27 changes: 0 additions & 27 deletions skore/src/skore/persistence/item/pickle_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from __future__ import annotations

from io import BytesIO
from traceback import format_exception
from typing import Any, Optional

import joblib
Expand Down Expand Up @@ -79,29 +78,3 @@ def object(self) -> Any:

with BytesIO(pickle_bytes) as stream:
return joblib.load(stream)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
try:
object = self.object
except Exception as e:
value = "Item cannot be displayed"
traceback = "".join(format_exception(None, e, e.__traceback__))
note = "".join(
(
(self.note or ""),
"\n\n",
"UnpicklingError with complete traceback:",
"\n\n",
traceback,
)
)
else:
value = f"```python\n{repr(object)}\n```"
note = self.note

return super().as_serializable_dict() | {
"media_type": "text/markdown",
"value": value,
"note": note,
}
14 changes: 0 additions & 14 deletions skore/src/skore/persistence/item/pillow_image_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from __future__ import annotations

from io import BytesIO
from typing import TYPE_CHECKING, Optional

from skore.persistence.item.item import Item, ItemTypeError
Expand Down Expand Up @@ -92,16 +91,3 @@ def image(self) -> PIL.Image.Image:
size=self.image_size,
data=image_bytes,
)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
with BytesIO() as stream:
self.image.save(stream, format="png")

png_bytes = stream.getvalue()
png_b64_str = bytes_to_b64_str(png_bytes)

return super().as_serializable_dict() | {
"media_type": "image/png;base64",
"value": png_b64_str,
}
11 changes: 0 additions & 11 deletions skore/src/skore/persistence/item/plotly_figure_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from skore.persistence.item.item import Item, ItemTypeError
from skore.persistence.item.media_item import lazy_is_instance
from skore.utils import bytes_to_b64_str

if TYPE_CHECKING:
import plotly.basedatatypes
Expand Down Expand Up @@ -76,13 +75,3 @@ def figure(self) -> plotly.basedatatypes.BaseFigure:
import plotly.io

return plotly.io.from_json(self.figure_str)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
figure_bytes = self.figure_str.encode("utf-8")
figure_b64_str = bytes_to_b64_str(figure_bytes)

return super().as_serializable_dict() | {
"media_type": "application/vnd.plotly.v1+json;base64",
"value": figure_b64_str,
}
7 changes: 0 additions & 7 deletions skore/src/skore/persistence/item/polars_dataframe_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,3 @@ def factory(cls, dataframe: polars.DataFrame, /, **kwargs) -> PolarsDataFrameIte
raise PolarsToJSONError("Conversion to JSON failed") from e

return cls(dataframe_json=dataframe_json, **kwargs)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
return super().as_serializable_dict() | {
"value": self.dataframe.to_pandas().fillna("NaN").to_dict(orient="tight"),
"media_type": "application/vnd.dataframe",
}
7 changes: 0 additions & 7 deletions skore/src/skore/persistence/item/polars_series_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,3 @@ def factory(cls, series: polars.Series, /, **kwargs) -> PolarsSeriesItem:
raise ItemTypeError(f"Type '{series.__class__}' is not supported.")

return cls(series_json=series.to_frame().write_json(), **kwargs)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
return super().as_serializable_dict() | {
"value": self.series.to_list(),
"media_type": "text/markdown",
}
7 changes: 0 additions & 7 deletions skore/src/skore/persistence/item/primitive_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,3 @@ def factory(cls, primitive: Primitive, /, **kwargs) -> PrimitiveItem:
raise ItemTypeError(f"Type '{primitive.__class__}' is not supported.")

return cls(primitive=primitive, **kwargs)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
return super().as_serializable_dict() | {
"media_type": "text/markdown",
"value": self.primitive,
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,3 @@ def factory(
estimator_skops_untrusted_types=estimator_skops_untrusted_types,
**kwargs,
)

def as_serializable_dict(self):
"""Convert item to a JSON-serializable dict to used by frontend."""
return super().as_serializable_dict() | {
"value": self.estimator_html_repr,
"media_type": "application/vnd.sklearn.estimator+html",
}
3 changes: 2 additions & 1 deletion skore/src/skore/ui/project_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from skore.persistence.item import Item
from skore.project.project import Project
from skore.ui.serializers import item_as_serializable

router = APIRouter(prefix="/project")

Expand All @@ -30,7 +31,7 @@ class SerializableItem:


def __item_as_serializable(name: str, item: Item, version: int) -> SerializableItem:
d = item.as_serializable_dict()
d = item_as_serializable(item)
return SerializableItem(
name=name,
media_type=d.get("media_type"),
Expand Down
Loading

0 comments on commit 7668219

Please sign in to comment.