Skip to content

Commit 66d3bea

Browse files
committed
[card/components] internal MetaflowCard classes support realtime behavior
- Markdown and Artifact Component made realtime. - default/blank cards made realtime
1 parent eba36be commit 66d3bea

File tree

3 files changed

+80
-10
lines changed

3 files changed

+80
-10
lines changed

metaflow/plugins/cards/card_modules/basic.py

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ def render(self):
146146
label=self._label,
147147
)
148148
datadict.update(img_dict)
149+
if self.id is not None:
150+
datadict["id"] = self.id
149151
return datadict
150152

151153

@@ -194,6 +196,8 @@ def render(self):
194196
datadict["columns"] = self._headers
195197
datadict["data"] = self._data
196198
datadict["vertical"] = self._vertical
199+
if self.id is not None:
200+
datadict["id"] = self.id
197201
return datadict
198202

199203

@@ -295,6 +299,8 @@ def __init__(self, title=None, subtitle=None, data={}):
295299
def render(self):
296300
datadict = super().render()
297301
datadict["data"] = self._data
302+
if self.id is not None:
303+
datadict["id"] = self.id
298304
return datadict
299305

300306

@@ -308,6 +314,8 @@ def __init__(self, text=None):
308314
def render(self):
309315
datadict = super().render()
310316
datadict["source"] = self._text
317+
if self.id is not None:
318+
datadict["id"] = self.id
311319
return datadict
312320

313321

@@ -319,7 +327,13 @@ class TaskInfoComponent(MetaflowCardComponent):
319327
"""
320328

321329
def __init__(
322-
self, task, page_title="Task Info", only_repr=True, graph=None, components=[]
330+
self,
331+
task,
332+
page_title="Task Info",
333+
only_repr=True,
334+
graph=None,
335+
components=[],
336+
runtime=False,
323337
):
324338
self._task = task
325339
self._only_repr = only_repr
@@ -328,6 +342,7 @@ def __init__(
328342
self._page_title = page_title
329343
self.final_component = None
330344
self.page_component = None
345+
self.runtime = runtime
331346

332347
def render(self):
333348
"""
@@ -340,7 +355,8 @@ def render(self):
340355
self._task, graph=self._graph
341356
)
342357
# ignore the name as an artifact
343-
del task_data_dict["data"]["name"]
358+
if "name" in task_data_dict["data"]:
359+
del task_data_dict["data"]["name"]
344360

345361
_metadata = dict(version=1, template="defaultCardTemplate")
346362
# try to parse out metaflow version from tags, but let it go if unset
@@ -370,11 +386,12 @@ def render(self):
370386
"Task Created On": task_data_dict["created_at"],
371387
"Task Finished On": task_data_dict["finished_at"],
372388
# Remove Microseconds from timedelta
373-
"Task Duration": str(self._task.finished_at - self._task.created_at).split(
374-
"."
375-
)[0],
376389
"Tags": ", ".join(tags),
377390
}
391+
if not self.runtime:
392+
task_metadata_dict["Task Duration"] = str(
393+
self._task.finished_at - self._task.created_at
394+
).split(".")[0]
378395
if len(user_info) > 0:
379396
task_metadata_dict["User"] = user_info[0].split("user:")[1]
380397

@@ -580,6 +597,10 @@ class DefaultCard(MetaflowCard):
580597

581598
ALLOW_USER_COMPONENTS = True
582599

600+
IS_RUNTIME_CARD = True
601+
602+
RELOAD_POLICY = MetaflowCard.RELOAD_POLICY_ONCHANGE
603+
583604
type = "default"
584605

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

592-
def render(self, task):
613+
def render(self, task, runtime=False):
593614
RENDER_TEMPLATE = read_file(RENDER_TEMPLATE_PATH)
594615
JS_DATA = read_file(JS_PATH)
595616
CSS_DATA = read_file(CSS_PATH)
@@ -598,6 +619,7 @@ def render(self, task):
598619
only_repr=self._only_repr,
599620
graph=self._graph,
600621
components=self._components,
622+
runtime=runtime,
601623
).render()
602624
pt = self._get_mustache()
603625
data_dict = dict(
@@ -611,11 +633,27 @@ def render(self, task):
611633
)
612634
return pt.render(RENDER_TEMPLATE, data_dict)
613635

636+
def render_runtime(self, task, data):
637+
return self.render(task, runtime=True)
638+
639+
def refresh(self, task, data):
640+
return data["components"]
641+
642+
def reload_content_token(self, task, data):
643+
if task.finished:
644+
return "final"
645+
else:
646+
return "runtime"
647+
614648

615649
class BlankCard(MetaflowCard):
616650

617651
ALLOW_USER_COMPONENTS = True
618652

653+
IS_RUNTIME_CARD = True
654+
655+
RELOAD_POLICY = MetaflowCard.RELOAD_POLICY_ONCHANGE
656+
619657
type = "blank"
620658

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

694+
def render_runtime(self, task, data):
695+
return self.render(task)
696+
697+
def refresh(self, task, data):
698+
return data["components"]
699+
700+
def reload_content_token(self, task, data):
701+
if task.finished:
702+
return "final"
703+
else:
704+
return "runtime"
705+
656706

657707
class TaskSpecCard(MetaflowCard):
658708
type = "taskspec_card"

metaflow/plugins/cards/card_modules/components.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ class Artifact(UserComponent):
3939
Use a truncated representation.
4040
"""
4141

42+
REALTIME_UPDATABLE = True
43+
44+
def update(self, artifact):
45+
self._artifact = artifact
46+
4247
def __init__(
4348
self, artifact: Any, name: Optional[str] = None, compressed: bool = True
4449
):
@@ -52,7 +57,9 @@ def render(self):
5257
artifact["name"] = None
5358
if self._name is not None:
5459
artifact["name"] = str(self._name)
55-
return ArtifactsComponent(data=[artifact]).render()
60+
af_component = ArtifactsComponent(data=[artifact])
61+
af_component.id = self.id
62+
return af_component.render()
5663

5764

5865
class Table(UserComponent):
@@ -434,9 +441,16 @@ class Markdown(UserComponent):
434441
Text formatted in Markdown.
435442
"""
436443

444+
REALTIME_UPDATABLE = True
445+
446+
def update(self, text=None):
447+
self._text = text
448+
437449
def __init__(self, text=None):
438450
self._text = text
439451

440452
@render_safely
441453
def render(self):
442-
return MarkdownComponent(self._text).render()
454+
comp = MarkdownComponent(self._text)
455+
comp.id = self.id
456+
return comp.render()

metaflow/plugins/cards/card_modules/convert_to_native_type.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def _full_classname(obj):
4444

4545

4646
class TaskToDict:
47-
def __init__(self, only_repr=False):
47+
def __init__(self, only_repr=False, runtime=False):
4848
# this dictionary holds all the supported functions
4949
import reprlib
5050
import pprint
@@ -59,6 +59,7 @@ def __init__(self, only_repr=False):
5959
r.maxlist = 100
6060
r.maxlevel = 3
6161
self._repr = r
62+
self._runtime = runtime
6263
self._only_repr = only_repr
6364
self._supported_types = {
6465
"tuple": self._parse_tuple,
@@ -90,11 +91,16 @@ def __call__(self, task, graph=None):
9091
stderr=task.stderr,
9192
stdout=task.stdout,
9293
created_at=task.created_at.strftime(TIME_FORMAT),
93-
finished_at=task.finished_at.strftime(TIME_FORMAT),
94+
finished_at=None,
9495
pathspec=task.pathspec,
9596
graph=graph,
9697
data={},
9798
)
99+
if not self._runtime:
100+
if task.finished_at is not None:
101+
task_dict.update(
102+
dict(finished_at=task.finished_at.strftime(TIME_FORMAT))
103+
)
98104
task_dict["data"], type_infered_objects = self._create_task_data_dict(task)
99105
task_dict.update(type_infered_objects)
100106
return task_dict

0 commit comments

Comments
 (0)