Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor improvements and bug fixes in bc-explorer tui #1343

Merged
merged 3 commits into from
Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions cmd/explorer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@
> docker exec -it -e "TERM=xterm-256color" ibet-wallet-api bash --login
> apl@2e5a80e06fcb:/$ ibet-explorer

Usage: ibet-explorer [OPTIONS] [URL] [LOT_SIZE]

╭─ Arguments ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ url [URL] ibet-Wallet-API server URL to connect [default: http://localhost:5000] │
│ lot_size [LOT_SIZE] Lot size to fetch Block Data list [default: 30] │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --install-completion [bash|zsh|fish|powershell|pwsh] Install completion for the specified shell. [default: None] │
│ --show-completion [bash|zsh|fish|powershell|pwsh] Show completion for the specified shell, to copy it or customize the installation. [default: None] │
│ --help Show this message and exit. │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Usage: ibet-explorer [OPTIONS]

╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --url TEXT ibet-Wallet-API server URL to connect [default: http://localhost:5000] │
│ --lot-size INTEGER Lot size to fetch Block Data list [default: 100] │
│ --install-completion [bash|zsh|fish|powershell|pwsh] Install completion for the specified shell. [default: None] │
│ --show-completion [bash|zsh|fish|powershell|pwsh] Show completion for the specified shell, to copy it or customize the installation. [default: None] │
│ --help Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```

- **URL**: ibet-Wallet-API URL.
Expand Down
15 changes: 5 additions & 10 deletions cmd/explorer/src/gui/explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,16 @@
SPDX-License-Identifier: Apache-2.0
"""
import os
import sys

from pydantic import ValidationError
from textual.app import App, ReturnType
from textual.binding import Binding

from connector import ApiNotEnabledException
from gui.screen.block import BlockScreen
from gui.screen.traceback import TracebackScreen
from gui.screen.transaction import TransactionScreen

from .error import Error

path = os.path.join(os.path.dirname(__file__), "../../../../")
sys.path.append(path)
from src.connector import ApiNotEnabledException
from src.gui.screen.block import BlockScreen
from src.gui.screen.traceback import TracebackScreen
from src.gui.screen.transaction import TransactionScreen
from src.gui.error import Error

from app.model.schema import ListBlockDataQuery, ListTxDataQuery

Expand Down
2 changes: 1 addition & 1 deletion cmd/explorer/src/gui/rendarable/block_detail_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from rich.table import Table
from rich.text import Text

from utils.time import human_time, unix_to_iso
from src.utils.time import human_time, unix_to_iso


class BlockDetailInfo:
Expand Down
3 changes: 2 additions & 1 deletion cmd/explorer/src/gui/rendarable/tx_detail_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@

SPDX-License-Identifier: Apache-2.0
"""
from app.model.schema import TxDataDetail
from rich.console import Group
from rich.panel import Panel
from rich.table import Table

from app.model.schema import TxDataDetail


class TxDetailInfo:
def __init__(self, tx_detail: TxDataDetail) -> None:
Expand Down
2 changes: 1 addition & 1 deletion cmd/explorer/src/gui/screen/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from textual.screen import Screen

if TYPE_CHECKING:
from gui.explorer import ExplorerApp
from src.gui.explorer import ExplorerApp


class TuiScreen(Screen):
Expand Down
196 changes: 112 additions & 84 deletions cmd/explorer/src/gui/screen/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,47 @@
SPDX-License-Identifier: Apache-2.0
"""
import asyncio
import os
import sys
from asyncio import Event, Lock
from datetime import datetime
from typing import Optional

from aiohttp import ClientSession, ClientTimeout, TCPConnector
from aiohttp import (
ClientSession,
ClientTimeout,
TCPConnector
)
from rich.text import Text
from textual.app import ComposeResult
from textual.binding import Binding
from textual.containers import Horizontal, Vertical
from textual.containers import (
Horizontal,
Vertical
)
from textual.reactive import Reactive
from textual.widgets import Button, DataTable, Footer, Label, Static

import connector
from gui.consts import ID
from gui.error import Error
from gui.screen.base import TuiScreen
from gui.widget.block_detail_view import BlockDetailView
from gui.widget.block_list_table import BlockListTable
from gui.widget.block_list_view import BlockListQueryPanel, BlockListSummaryPanel, BlockListView
from gui.widget.menu import Menu, MenuInstruction
from gui.widget.query_panel import QuerySetting
from textual.widgets import (
Button,
DataTable,
Footer,
Label,
Static
)

path = os.path.join(os.path.dirname(__file__), "../../../../../")
sys.path.append(path)
from src import connector
from src.gui.consts import ID
from src.gui.error import Error
from src.gui.screen.base import TuiScreen
from src.gui.widget.block_detail_view import BlockDetailView
from src.gui.widget.block_list_table import BlockListTable
from src.gui.widget.block_list_view import (
BlockListQueryPanel,
BlockListSummaryPanel,
BlockListView
)
from src.gui.widget.menu import (
Menu,
MenuInstruction
)
from src.gui.widget.query_panel import QuerySetting

from app.model.schema import (
BlockDataDetail,
Expand Down Expand Up @@ -89,12 +104,19 @@ def compose(self) -> ComposeResult:
yield Menu(id=ID.MENU)
yield QuerySetting(id=ID.QUERY_PANEL)

def update_current_block(self, latest_block_number: int):
self.query_one(f"#{ID.BLOCK_CURRENT_BLOCK_NUMBER}", Static).update(f"Current Block: {latest_block_number}")
##################################################
# Event
##################################################

def update_is_synced(self, is_synced: bool):
self.query_one(f"#{ID.BLOCK_IS_SYNCED}", Static).update(f"Is Synced: {is_synced}")
self.query_one(f"#{ID.BLOCK_NOTION}", Static).update(f"Press [E] To Load Block List")
async def on_mount(self) -> None:
"""
Occurs when Self is mounted
"""
self.query_one(Menu).hide()
self.remove_class("menu")
self.query_one(QuerySetting).hide()
self.query(BlockListTable)[0].focus()
self.set_interval(self.refresh_rate, self.fetch_sync_status)

async def on_button_pressed(self, event: Button.Pressed) -> None:
"""
Expand All @@ -116,25 +138,65 @@ async def on_button_pressed(self, event: Button.Pressed) -> None:
self.tui.state.tx_list_query = get_query
await self.app.push_screen("transaction_screen")
case ID.QUERY_PANEL_ENTER:
self.action_reload_block()
self.reload_block()

async def on_query_setting_enter(self, event: QuerySetting.Enter):
"""
Occurs when QuerySetting.Enter is emitted
"""
event.stop()
event.prevent_default()
self.action_reload_block()
self.reload_block()

async def on_mount(self) -> None:
async def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
"""
Occurs when Self is mounted
Occurs when DataTable row is selected
"""
self.query_one(Menu).hide()
self.remove_class("menu")
self.query_one(QuerySetting).hide()
self.query(BlockListTable)[0].focus()
self.set_interval(self.refresh_rate, self.fetch_sync_status)
event.stop()
event.prevent_default()
selected_row = self.query_one(BlockListTable).data.get(event.cursor_row)
if selected_row is None:
return
await self.fetch_block_detail(selected_row[0])

if int(selected_row[2]) == 0:
# If the number of transaction is 0, menu is not pop up.
return

self.query_one(Menu).show(
MenuInstruction(
block_number=selected_row[0],
block_hash=selected_row[3],
selected_row=event.cursor_row
)
)
self.add_class("menu")
self.query(BlockListTable)[0].can_focus = False

def reload_block(self) -> None:
if self.tui.state.current_block_number is None or self.tui.state.block_list_query is None:
return

self.query_one(BlockListQueryPanel).block_list_query = self.tui.state.block_list_query
asyncio.create_task(self.fetch_block_list())

##################################################
# Key biding
##################################################
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“binding” typo I think.


def action_edit_query(self) -> None:
"""
Occurs when keybind related to `edit_query` is called.
"""
if self.tui.state.current_block_number is None or self.tui.state.block_list_query is None:
return

self.query_one(QuerySetting).show()
self.query(BlockListTable)[0].can_focus = False

##################################################
# Fetch data
##################################################

async def fetch_sync_status(self):
async with TCPConnector(limit=2, keepalive_timeout=0) as tcp_connector:
Expand All @@ -158,41 +220,6 @@ async def fetch_sync_status(self):

self.tui.state.current_block_number = node_info.latest_block_number

async def fetch_block_detail(self, block_number: int):
async with TCPConnector(limit=1, keepalive_timeout=0) as tcp_connector:
async with ClientSession(connector=tcp_connector, timeout=ClientTimeout(30)) as session:
try:
block_detail: BlockDataDetail = await connector.get_block_data(
session,
self.base_url,
block_number,
)
except Exception as e:
if hasattr(self, "emit_no_wait"):
self.emit_no_wait(Error(e, self))
return
self.query_one(BlockDetailView).block_detail = block_detail

def action_edit_query(self) -> None:
"""
Occurs when keybind related to `edit_query` is called.
"""
if self.tui.state.current_block_number is None or self.tui.state.block_list_query is None:
return

self.query_one(QuerySetting).show()
self.query(BlockListTable)[0].can_focus = False

def action_reload_block(self) -> None:
"""
Occurs when keybind related to `reload_block` is called.
"""
if self.tui.state.current_block_number is None or self.tui.state.block_list_query is None:
return

self.query_one(BlockListQueryPanel).block_list_query = self.tui.state.block_list_query
asyncio.create_task(self.fetch_block_list())

async def fetch_block_list(self) -> None:
if self.tui.state.current_block_number == 0:
return
Expand All @@ -215,23 +242,24 @@ async def fetch_block_list(self) -> None:
finally:
self.query_one(BlockListSummaryPanel).loading = False

async def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
"""
Occurs when DataTable row is selected
"""
event.stop()
event.prevent_default()
selected_row = self.query_one(BlockListTable).data.get(event.cursor_row)
if selected_row is None:
return
await self.fetch_block_detail(selected_row[0])
async def fetch_block_detail(self, block_number: int):
async with TCPConnector(limit=1, keepalive_timeout=0) as tcp_connector:
async with ClientSession(connector=tcp_connector, timeout=ClientTimeout(30)) as session:
try:
block_detail: BlockDataDetail = await connector.get_block_data(
session,
self.base_url,
block_number,
)
except Exception as e:
if hasattr(self, "emit_no_wait"):
self.emit_no_wait(Error(e, self))
return
self.query_one(BlockDetailView).block_detail = block_detail

if int(selected_row[2]) == 0:
# If the number of transaction is 0, menu is not pop up.
return
def update_current_block(self, latest_block_number: int):
self.query_one(f"#{ID.BLOCK_CURRENT_BLOCK_NUMBER}", Static).update(f"Current Block: {latest_block_number}")

self.query_one(Menu).show(
MenuInstruction(block_number=selected_row[0], block_hash=selected_row[3], selected_row=event.cursor_row)
)
self.add_class("menu")
self.query(BlockListTable)[0].can_focus = False
def update_is_synced(self, is_synced: bool):
self.query_one(f"#{ID.BLOCK_IS_SYNCED}", Static).update(f"Is Synced: {is_synced}")
self.query_one(f"#{ID.BLOCK_NOTION}", Static).update(f"Press [E] To Load Block List")
14 changes: 11 additions & 3 deletions cmd/explorer/src/gui/screen/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
from textual.binding import Binding
from textual.widgets import Footer

from gui.screen.base import TuiScreen
from gui.widget.block_list_table import BlockListTable
from gui.widget.traceback import TracebackWidget
from src.gui.screen.base import TuiScreen
from src.gui.widget.block_list_table import BlockListTable
from src.gui.widget.traceback import TracebackWidget


class TracebackScreen(TuiScreen):
Expand All @@ -32,12 +32,20 @@ def compose(self) -> ComposeResult:
yield TracebackWidget()
yield Footer()

##################################################
# Event
##################################################

async def on_mount(self) -> None:
"""
Occurs when Self is mounted
"""
self.query(TracebackWidget)[0].focus()

##################################################
# Key biding
##################################################

def action_quit(self):
"""
Occurs when keybind related to `quit` is called.
Expand Down
Loading