diff --git a/flowshow/__init__.py b/flowshow/__init__.py index b02a5f4..6127a2a 100644 --- a/flowshow/__init__.py +++ b/flowshow/__init__.py @@ -114,14 +114,22 @@ def _safe_serialize_artifacts(self) -> Dict[str, Any]: return result def to_dataframe(self): - import pandas as pd - return pd.DataFrame(flatten_tasks(self.to_dict())) + import polars as pl + return pl.from_records(flatten_tasks(self.to_dict())) def render(self): template_path = Path(__file__).parent / "templates/index.html" template = Template(template_path.read_text()) return template.render(data=self.to_dict()) - + + def save_render(self, path: Union[str, Path] = "task_run.html") -> None: + """Render the task run to HTML and save it to the specified path.""" + path_str = path + if isinstance(path, Path): + path_str = str(path) + + with open(path_str, 'w') as f: + f.write(self.render()) @contextmanager def _task_run_context(run: TaskRun): @@ -336,6 +344,9 @@ async def _async_call(self, *args, **kwargs): run._log("ERROR", error_msg) return result + + def save_render(self, path: Union[str, Path] = "task_run.html") -> None: + self.last_run.save_render(path) def task( diff --git a/tests/test_basics.py b/tests/test_basics.py index 454d8e5..aaf421c 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -135,3 +135,23 @@ def error_task(): assert error_data["task_name"] == "error_task" assert "Expected error" in error_data["error"] assert error_data["error_traceback"] is not None # Traceback should be present + + +def test_save_rander(): + @task + def sample_task(): + return "Sample task completed" + + # Run the task to generate a last_run + sample_task() + + # Save the rendered HTML to a file + sample_task.last_run.save_render("test_task_run.html") + + # Check if the file was created + import os + assert os.path.exists("test_task_run.html") + + # Clean up the generated file + os.remove("test_task_run.html") +