Team Ai
Apppublic

WalisonCruz/function-gemma

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
table.py176 linesDownload Raw Back to root
1import os2from typing import Any, Literal3 4from pandas import DataFrame5 6try:7    from trackio.media.media import TrackioMedia8    from trackio.utils import MEDIA_DIR9except ImportError:10    from media.media import TrackioMedia11    from utils import MEDIA_DIR12 13 14class Table:15    """16    Initializes a Table object.17 18    Tables can be used to log tabular data including images, numbers, and text.19 20    Args:21        columns (`list[str]`, *optional*):22            Names of the columns in the table. Optional if `data` is provided. Not23            expected if `dataframe` is provided. Currently ignored.24        data (`list[list[Any]]`, *optional*):25            2D row-oriented array of values. Each value can be a number, a string26            (treated as Markdown and truncated if too long), or a `Trackio.Image` or27            list of `Trackio.Image` objects.28        dataframe (`pandas.DataFrame`, *optional*):29            DataFrame used to create the table. When set, `data` and `columns`30            arguments are ignored.31        rows (`list[list[Any]]`, *optional*):32            Currently ignored.33        optional (`bool` or `list[bool]`, *optional*, defaults to `True`):34            Currently ignored.35        allow_mixed_types (`bool`, *optional*, defaults to `False`):36            Currently ignored.37        log_mode: (`Literal["IMMUTABLE", "MUTABLE", "INCREMENTAL"]` or `None`, *optional*, defaults to `"IMMUTABLE"`):38            Currently ignored.39    """40 41    TYPE = "trackio.table"42 43    def __init__(44        self,45        columns: list[str] | None = None,46        data: list[list[Any]] | None = None,47        dataframe: DataFrame | None = None,48        rows: list[list[Any]] | None = None,49        optional: bool | list[bool] = True,50        allow_mixed_types: bool = False,51        log_mode: Literal["IMMUTABLE", "MUTABLE", "INCREMENTAL"] | None = "IMMUTABLE",52    ):53        # TODO: implement support for columns, dtype, optional, allow_mixed_types, and log_mode.54        # for now (like `rows`) they are included for API compat but don't do anything.55        if dataframe is None:56            self.data = DataFrame(data) if data is not None else DataFrame()57        else:58            self.data = dataframe59 60    def _has_media_objects(self, dataframe: DataFrame) -> bool:61        """Check if dataframe contains any TrackioMedia objects or lists of TrackioMedia objects."""62        for col in dataframe.columns:63            if dataframe[col].apply(lambda x: isinstance(x, TrackioMedia)).any():64                return True65            if (66                dataframe[col]67                .apply(68                    lambda x: isinstance(x, list)69                    and len(x) > 070                    and isinstance(x[0], TrackioMedia)71                )72                .any()73            ):74                return True75        return False76 77    def _process_data(self, project: str, run: str, step: int = 0):78        """Convert dataframe to dict format, processing any TrackioMedia objects if present."""79        df = self.data80        if not self._has_media_objects(df):81            return df.to_dict(orient="records")82 83        processed_df = df.copy()84        for col in processed_df.columns:85            for idx in processed_df.index:86                value = processed_df.at[idx, col]87                if isinstance(value, TrackioMedia):88                    value._save(project, run, step)89                    processed_df.at[idx, col] = value._to_dict()90                if (91                    isinstance(value, list)92                    and len(value) > 093                    and isinstance(value[0], TrackioMedia)94                ):95                    [v._save(project, run, step) for v in value]96                    processed_df.at[idx, col] = [v._to_dict() for v in value]97 98        return processed_df.to_dict(orient="records")99 100    @staticmethod101    def to_display_format(table_data: list[dict]) -> list[dict]:102        """103        Converts stored table data to display format for UI rendering.104 105        Note:106            This does not use the `self.data` attribute, but instead uses the107            `table_data` parameter, which is what the UI receives.108 109        Args:110            table_data (`list[dict]`):111                List of dictionaries representing table rows (from stored `_value`).112 113        Returns:114            `list[dict]`: Table data with images converted to markdown syntax and long115            text truncated.116        """117        truncate_length = int(os.getenv("TRACKIO_TABLE_TRUNCATE_LENGTH", "250"))118 119        def convert_image_to_markdown(image_data: dict) -> str:120            relative_path = image_data.get("file_path", "")121            caption = image_data.get("caption", "")122            absolute_path = MEDIA_DIR / relative_path123            return f'<img src="/gradio_api/file={absolute_path}" alt="{caption}" />'124 125        processed_data = []126        for row in table_data:127            processed_row = {}128            for key, value in row.items():129                if isinstance(value, dict) and value.get("_type") == "trackio.image":130                    processed_row[key] = convert_image_to_markdown(value)131                elif (132                    isinstance(value, list)133                    and len(value) > 0134                    and isinstance(value[0], dict)135                    and value[0].get("_type") == "trackio.image"136                ):137                    # This assumes that if the first item is an image, all items are images. Ok for now since we don't support mixed types in a single cell.138                    processed_row[key] = (139                        '<div style="display: flex; gap: 10px;">'140                        + "".join([convert_image_to_markdown(item) for item in value])141                        + "</div>"142                    )143                elif isinstance(value, str) and len(value) > truncate_length:144                    truncated = value[:truncate_length]145                    full_text = value.replace("<", "&lt;").replace(">", "&gt;")146                    processed_row[key] = (147                        f'<details style="display: inline;">'148                        f'<summary style="display: inline; cursor: pointer;">{truncated}…<span><em>(truncated, click to expand)</em></span></summary>'149                        f'<div style="margin-top: 10px; padding: 10px; background: #f5f5f5; border-radius: 4px; max-height: 400px; overflow: auto;">'150                        f'<pre style="white-space: pre-wrap; word-wrap: break-word; margin: 0;">{full_text}</pre>'151                        f"</div>"152                        f"</details>"153                    )154                else:155                    processed_row[key] = value156            processed_data.append(processed_row)157        return processed_data158 159    def _to_dict(self, project: str, run: str, step: int = 0):160        """161        Converts the table to a dictionary representation.162 163        Args:164            project (`str`):165                Project name for saving media files.166            run (`str`):167                Run name for saving media files.168            step (`int`, *optional*, defaults to `0`):169                Step number for saving media files.170        """171        data = self._process_data(project, run, step)172        return {173            "_type": self.TYPE,174            "_value": data,175        }176