Skip to content

Commit

Permalink
[card/components] internal MetaflowCard classes support realtime beha…
Browse files Browse the repository at this point in the history
…vior

- Markdown and Artifact Component made realtime.
- default/blank cards made realtime
  • Loading branch information
valayDave committed Oct 18, 2023
1 parent d5f8c75 commit bb156d6
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 10 deletions.
62 changes: 56 additions & 6 deletions metaflow/plugins/cards/card_modules/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ def render(self):
label=self._label,
)
datadict.update(img_dict)
if self.component_id is not None:
datadict["id"] = self.component_id
return datadict


Expand Down Expand Up @@ -194,6 +196,8 @@ def render(self):
datadict["columns"] = self._headers
datadict["data"] = self._data
datadict["vertical"] = self._vertical
if self.component_id is not None:
datadict["id"] = self.component_id
return datadict


Expand Down Expand Up @@ -295,6 +299,8 @@ def __init__(self, title=None, subtitle=None, data={}):
def render(self):
datadict = super().render()
datadict["data"] = self._data
if self.component_id is not None:
datadict["id"] = self.component_id
return datadict


Expand All @@ -308,6 +314,8 @@ def __init__(self, text=None):
def render(self):
datadict = super().render()
datadict["source"] = self._text
if self.component_id is not None:
datadict["id"] = self.component_id
return datadict


Expand All @@ -319,7 +327,13 @@ class TaskInfoComponent(MetaflowCardComponent):
"""

def __init__(
self, task, page_title="Task Info", only_repr=True, graph=None, components=[]
self,
task,
page_title="Task Info",
only_repr=True,
graph=None,
components=[],
runtime=False,
):
self._task = task
self._only_repr = only_repr
Expand All @@ -328,6 +342,7 @@ def __init__(
self._page_title = page_title
self.final_component = None
self.page_component = None
self.runtime = runtime

def render(self):
"""
Expand All @@ -340,7 +355,8 @@ def render(self):
self._task, graph=self._graph
)
# ignore the name as an artifact
del task_data_dict["data"]["name"]
if "name" in task_data_dict["data"]:
del task_data_dict["data"]["name"]

_metadata = dict(version=1, template="defaultCardTemplate")
# try to parse out metaflow version from tags, but let it go if unset
Expand Down Expand Up @@ -370,11 +386,12 @@ def render(self):
"Task Created On": task_data_dict["created_at"],
"Task Finished On": task_data_dict["finished_at"],
# Remove Microseconds from timedelta
"Task Duration": str(self._task.finished_at - self._task.created_at).split(
"."
)[0],
"Tags": ", ".join(tags),
}
if not self.runtime:
task_metadata_dict["Task Duration"] = str(
self._task.finished_at - self._task.created_at
).split(".")[0]
if len(user_info) > 0:
task_metadata_dict["User"] = user_info[0].split("user:")[1]

Expand Down Expand Up @@ -580,6 +597,10 @@ class DefaultCard(MetaflowCard):

ALLOW_USER_COMPONENTS = True

RUNTIME_UPDATABLE = True

RELOAD_POLICY = MetaflowCard.RELOAD_POLICY_ONCHANGE

type = "default"

def __init__(self, options=dict(only_repr=True), components=[], graph=None):
Expand All @@ -589,7 +610,7 @@ def __init__(self, options=dict(only_repr=True), components=[], graph=None):
self._only_repr = options["only_repr"]
self._components = components

def render(self, task):
def render(self, task, runtime=False):
RENDER_TEMPLATE = read_file(RENDER_TEMPLATE_PATH)
JS_DATA = read_file(JS_PATH)
CSS_DATA = read_file(CSS_PATH)
Expand All @@ -598,6 +619,7 @@ def render(self, task):
only_repr=self._only_repr,
graph=self._graph,
components=self._components,
runtime=runtime,
).render()
pt = self._get_mustache()
data_dict = dict(
Expand All @@ -611,11 +633,27 @@ def render(self, task):
)
return pt.render(RENDER_TEMPLATE, data_dict)

def render_runtime(self, task, data):
return self.render(task, runtime=True)

def refresh(self, task, data):
return data["components"]

def reload_content_token(self, task, data):
if task.finished:
return "final"
else:
return "runtime"


class BlankCard(MetaflowCard):

ALLOW_USER_COMPONENTS = True

RUNTIME_UPDATABLE = True

RELOAD_POLICY = MetaflowCard.RELOAD_POLICY_ONCHANGE

type = "blank"

def __init__(self, options=dict(title=""), components=[], graph=None):
Expand Down Expand Up @@ -653,6 +691,18 @@ def render(self, task, components=[]):
)
return pt.render(RENDER_TEMPLATE, data_dict)

def render_runtime(self, task, data):
return self.render(task)

def refresh(self, task, data):
return data["components"]

def reload_content_token(self, task, data):
if task.finished:
return "final"
else:
return "runtime"


class TaskSpecCard(MetaflowCard):
type = "taskspec_card"
Expand Down
18 changes: 16 additions & 2 deletions metaflow/plugins/cards/card_modules/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ class Artifact(UserComponent):
Use a truncated representation.
"""

REALTIME_UPDATABLE = True

def update(self, artifact):
self._artifact = artifact

def __init__(
self, artifact: Any, name: Optional[str] = None, compressed: bool = True
):
Expand All @@ -52,7 +57,9 @@ def render(self):
artifact["name"] = None
if self._name is not None:
artifact["name"] = str(self._name)
return ArtifactsComponent(data=[artifact]).render()
af_component = ArtifactsComponent(data=[artifact])
af_component.component_id = self.component_id
return af_component.render()


class Table(UserComponent):
Expand Down Expand Up @@ -434,9 +441,16 @@ class Markdown(UserComponent):
Text formatted in Markdown.
"""

REALTIME_UPDATABLE = True

def update(self, text=None):
self._text = text

def __init__(self, text=None):
self._text = text

@render_safely
def render(self):
return MarkdownComponent(self._text).render()
comp = MarkdownComponent(self._text)
comp.component_id = self.component_id
return comp.render()
10 changes: 8 additions & 2 deletions metaflow/plugins/cards/card_modules/convert_to_native_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def _full_classname(obj):


class TaskToDict:
def __init__(self, only_repr=False):
def __init__(self, only_repr=False, runtime=False):
# this dictionary holds all the supported functions
import reprlib
import pprint
Expand All @@ -59,6 +59,7 @@ def __init__(self, only_repr=False):
r.maxlist = 100
r.maxlevel = 3
self._repr = r
self._runtime = runtime
self._only_repr = only_repr
self._supported_types = {
"tuple": self._parse_tuple,
Expand Down Expand Up @@ -90,11 +91,16 @@ def __call__(self, task, graph=None):
stderr=task.stderr,
stdout=task.stdout,
created_at=task.created_at.strftime(TIME_FORMAT),
finished_at=task.finished_at.strftime(TIME_FORMAT),
finished_at=None,
pathspec=task.pathspec,
graph=graph,
data={},
)
if not self._runtime:
if task.finished_at is not None:
task_dict.update(
dict(finished_at=task.finished_at.strftime(TIME_FORMAT))
)
task_dict["data"], type_infered_objects = self._create_task_data_dict(task)
task_dict.update(type_infered_objects)
return task_dict
Expand Down

0 comments on commit bb156d6

Please sign in to comment.