Skip to content

Commit

Permalink
tui: show view
Browse files Browse the repository at this point in the history
* Add `cylc show` support for tasks in Tui.
  • Loading branch information
oliver-sanders committed Nov 8, 2023
1 parent f884bc8 commit 45c975a
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 3 deletions.
15 changes: 13 additions & 2 deletions cylc/flow/tui/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ def cli_cmd(*cmd, ret=False):
return _out


def _show(id_):
"""Special mutation to display cylc show output."""
# dynamic import to avoid circular import issues
from cylc.flow.tui.overlay import text_box
return partial(
text_box,
text=cli_cmd('show', id_, '--color=never', ret=True),
)


def _log(id_):
"""Special mutation to open the log view."""
# dynamic import to avoid circular import issues
Expand Down Expand Up @@ -247,6 +257,7 @@ def _list_log_files(id_):
},
'task': {
'log': _log,
'show': _show,
},
'job': {
'log': _log,
Expand Down Expand Up @@ -421,13 +432,13 @@ def offline_mutate(mutation, selection):
id_ = Tokens(job, relative=True).duplicate(
workflow=variables['workflow'][0]
)
return OFFLINE_MUTATIONS['workflow'][mutation](id_.id)
return OFFLINE_MUTATIONS['job'][mutation](id_.id)
if 'task' in variables:
for task in variables['task']:
id_ = Tokens(task, relative=True).duplicate(
workflow=variables['workflow'][0]
)
return OFFLINE_MUTATIONS['workflow'][mutation](id_.id)
return OFFLINE_MUTATIONS['task'][mutation](id_.id)
if 'workflow' in variables:
for workflow in variables['workflow']:
return OFFLINE_MUTATIONS['workflow'][mutation](workflow)
Expand Down
13 changes: 13 additions & 0 deletions cylc/flow/tui/overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
)
from cylc.flow.tui.util import (
get_task_icon,
get_text_dimensions,
)


Expand Down Expand Up @@ -456,3 +457,15 @@ def open_log(*_, filename=None, close=False):
# open full screen
{'width': 9999, 'height': 9999}
)


def text_box(app, text=''):
"""A simple text box overlay."""
width, height = get_text_dimensions(text)
return (
urwid.ListBox([
urwid.Text(text),
]),
# NOTE: those fudge factors account for the overlay border & padding
{'width': width + 4, 'height': height + 6}
)
22 changes: 22 additions & 0 deletions cylc/flow/tui/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from itertools import zip_longest
import re
from time import time
from typing import Tuple

from cylc.flow import LOG
from cylc.flow.id import Tokens
Expand Down Expand Up @@ -554,3 +555,24 @@ def extract_context(selection):
if value not in lst:
lst.append(value)
return ret


def get_text_dimensions(text: str) -> Tuple[int, int]:
"""Return the monospace size of a block of multiline text.
Examples:
>>> get_text_dimensions('foo')
(3, 1)
>>> get_text_dimensions('''
... foo bar
... baz
... ''')
(11, 3)
>>> get_text_dimensions('')
(0, 0)
"""
lines = text.splitlines()
return max((0, *(len(line) for line in lines))), len(lines)
41 changes: 41 additions & 0 deletions tests/integration/tui/screenshots/test_show.fail.html

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions tests/integration/tui/screenshots/test_show.success.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<pre><span style="color:#ff0000;background:#e5e5e5;font-weight:bold">C</span><span style="color:#ffff00;background:#e5e5e5;font-weight:bold">y</span><span style="color:#00ff00;background:#e5e5e5;font-weight:bold">l</span><span style="color:#5c5cff;background:#e5e5e5;font-weight:bold">c</span><span style="color:#000000;background:#e5e5e5;font-weight:bold"> Tui</span><span style="color:#7f7f7f;background:#e5e5e5"> workflows filtered (</span><span style="color:#7f7f7f;background:#e5e5e5;font-weight:bold">W</span><span style="color:#7f7f7f;background:#e5e5e5"> - edit, </span><span style="color:#7f7f7f;background:#e5e5e5;font-weight:bold">E</span><span style="color:#7f7f7f;background:#e5e5e5"> - reset)</span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5">-</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">~cylc </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">-</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5;font-weight:bold">one</span><span style="color:#000000;background:#e5e5e5"> - </span><span style="color:#cdcd00;background:#e5e5e5">paused</span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">-</span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">̿○ 1 </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">̿○ foo </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5">────────────────────────</span><span style="color:#000000;background:#e5e5e5">────────────────────────</span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">title: Foo </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">description: The first metasyntactic </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">variable. </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">URL: (not given) </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">state: waiting </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">prerequisites: (None) </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5">outputs: ('-': not completed) </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> - 1/foo expired </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> - 1/foo submitted </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> - 1/foo submit-failed </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> - 1/foo started </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> - 1/foo succeeded </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> - 1/foo failed </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> q to close </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5">────────────────────────────────────────────────</span><span style="color:#000000;background:#e5e5e5"></span><span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#000000;background:#e5e5e5"> </span>
<span style="color:#ffffff;background:#0000ee">quit: </span><span style="color:#00ffff;background:#0000ee">q</span><span style="color:#ffffff;background:#0000ee"> help: </span><span style="color:#00ffff;background:#0000ee">h</span><span style="color:#ffffff;background:#0000ee"> context: </span><span style="color:#00ffff;background:#0000ee">enter</span><span style="color:#ffffff;background:#0000ee"> tree: </span><span style="color:#00ffff;background:#0000ee">- ← + → </span><span style="color:#ffffff;background:#0000ee"> navigation: </span><span style="color:#00ffff;background:#0000ee">↑ ↓ ↥ ↧ Home End </span><span style="color:#ffffff;background:#0000ee"> </span>
<span style="color:#ffffff;background:#0000ee">filter tasks: </span><span style="color:#00ffff;background:#0000ee">T f s r R </span><span style="color:#ffffff;background:#0000ee"> filter workflows: </span><span style="color:#00ffff;background:#0000ee">W E p </span><span style="color:#ffffff;background:#0000ee"> </span>
</pre>
2 changes: 1 addition & 1 deletion tests/integration/tui/test_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ async def workflow(mod_flow, mod_scheduler, mod_start, standarise_host_and_path)
job_2_err = get_job_log(job_2, 'job.err')
with open(job_2_err, 'w+') as log:
log.write(f'job: {job_2.relative_id}\nthis is a job error\n')

# 1/a/NN -> 1/a/02
(job_2_out.parent.parent / 'NN').symlink_to(
(job_2_out.parent.parent / '02'),
Expand Down
70 changes: 70 additions & 0 deletions tests/integration/tui/test_show.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
# THIS FILE IS PART OF THE CYLC WORKFLOW ENGINE.
# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from cylc.flow.exceptions import ClientError
from cylc.flow.tui.data import _show


async def test_show(flow, scheduler, start, raikura, monkeypatch):
"""Test "cylc show" support in Tui."""
id_ = flow({
'scheduling': {
'graph': {
'R1': 'foo'
},
},
'runtime': {
'foo': {
'meta': {
'title': 'Foo',
'description': 'The first metasyntactic variable.'
},
},
},
}, name='one')
schd = scheduler(id_)
async with start(schd):
await schd.update_data_structure()

with raikura(size='80,40') as rk:
rk.user_input('down', 'right')
rk.wait_until_loaded(schd.tokens.id)

# select a task
rk.user_input('down', 'down', 'enter')

# select the "show" context option
rk.user_input(*(['down'] * 6), 'enter')
rk.compare_screenshot(
'success',
'the show output should be displayed',
)

# make it look like "cylc show" failed
def cli_cmd_fail(*args, **kwargs):
raise ClientError(':(')
monkeypatch.setattr(
'cylc.flow.tui.data.cli_cmd',
cli_cmd_fail,
)

# select the "show" context option
rk.user_input('q', 'enter', *(['down'] * 6), 'enter')
rk.compare_screenshot(
'fail',
'the error should be displayed',
)

0 comments on commit 45c975a

Please sign in to comment.