Skip to content

trading_agent.terminal.widgets.question_card

The card for a desk's questions (ask_user), as the web page's QuestionCard.tsx shows them.

One to four questions, each with two to four options and an "Other" answer. With several questions there is one tab per question and a final tab to review and submit. A single question with a single choice is sent as soon as an option is picked.

Keys: Up and Down move, Enter or a number picks (a multiple-choice question ticks and unticks), the number after the last option goes to the Other box, Tab and Shift+Tab move between questions, Ctrl+S submits, Ctrl+X dismisses without answering.

Typical usage example:

card = QuestionCard(request, gateway)

QuestionCard

QuestionCard(request, gateway)

Bases: Vertical

A card for answering the desk's questions.

Creates the card.

Parameters:

Name Type Description Default
request dict[str, Any]

The pending question request.

required
gateway GatewayClient

The gateway, to send the answers.

required
Source code in src/trading_agent/terminal/widgets/question_card.py
def __init__(
    self,
    request: dict[str, Any],
    gateway: gateway_client.GatewayClient,
):
    """Creates the card.

    Args:
        request (dict[str, Any]): The pending question request.
        gateway (gateway_client.GatewayClient): The gateway, to send the answers.
    """
    super().__init__(classes='request-card question-card')
    self.request_id = request['request_id']
    self._request = request
    self._gateway = gateway
    content = request.get('content') or {}
    self._questions: list[dict[str, Any]] = list(
        content.get('questions') or []
    )
    self._drafts: list[dict[str, Any]] = []
    for _ in self._questions:
        self._drafts.append(
            {
                'selected': [],
                'other': '',
            }
        )
    self._tab = 0
    self._sending = False

Done

Bases: Message

The request was answered or dismissed.

compose

compose()

Builds the card.

Yields:

Type Description
Widget

The heading, tabs, question, options, Other box, preview, review, error and hint.

Source code in src/trading_agent/terminal/widgets/question_card.py
def compose(self):
    """Builds the card.

    Yields:
        Widget: The heading, tabs, question, options, Other box, preview, review, error and hint.
    """
    yield widgets.Static('', classes='request-heading')
    yield widgets.Static('', classes='question-tabs')
    yield widgets.Static('', classes='question-text')
    yield widgets.OptionList(classes='question-options')
    yield widgets.Input(
        placeholder='Other: type your own answer',
        classes='question-other',
        select_on_focus=False,
    )
    yield widgets.Markdown('', classes='question-preview')
    yield widgets.Static('', classes='question-review')
    yield widgets.Static('', classes='request-error')
    yield widgets.Static('', classes='request-hint')

on_mount

on_mount()

Shows the first question.

Source code in src/trading_agent/terminal/widgets/question_card.py
def on_mount(self) -> None:
    """Shows the first question."""
    self._show()

focus_card

focus_card()

Puts the focus on the options, so the card's keys work.

Source code in src/trading_agent/terminal/widgets/question_card.py
def focus_card(self) -> None:
    """Puts the focus on the options, so the card's keys work."""
    if self._reviewing():
        self.focus()
        return
    self.query_one('.question-options', widgets.OptionList).focus()

on_key

on_key(event)

Picks an option with its number key.

Parameters:

Name Type Description Default
event Key

The key press.

required
Source code in src/trading_agent/terminal/widgets/question_card.py
def on_key(self, event: events.Key) -> None:
    """Picks an option with its number key.

    Args:
        event (events.Key): The key press.
    """
    if isinstance(self.screen.focused, widgets.Input):
        return
    question = self._question()
    if question is None or not event.character:
        return
    if not event.character.isdigit():
        return
    number = int(event.character)
    options = question.get('options') or []
    if 1 <= number <= len(options):
        event.stop()
        self.query_one(
            '.question-options', widgets.OptionList
        ).highlighted = number - 1
        self._choose(options[number - 1]['label'])
    elif number == len(options) + 1:
        event.stop()
        self.query_one('.question-other', widgets.Input).focus()

on_option_list_option_highlighted

on_option_list_option_highlighted(event)

Shows the highlighted option's preview.

Parameters:

Name Type Description Default
event OptionHighlighted

The highlight.

required
Source code in src/trading_agent/terminal/widgets/question_card.py
def on_option_list_option_highlighted(
    self, event: widgets.OptionList.OptionHighlighted
) -> None:
    """Shows the highlighted option's preview.

    Args:
        event (widgets.OptionList.OptionHighlighted): The highlight.
    """
    event.stop()
    self._show_preview(event.option_index)

on_option_list_option_selected

on_option_list_option_selected(event)

Picks the selected option.

Parameters:

Name Type Description Default
event OptionSelected

The selection.

required
Source code in src/trading_agent/terminal/widgets/question_card.py
def on_option_list_option_selected(
    self, event: widgets.OptionList.OptionSelected
) -> None:
    """Picks the selected option.

    Args:
        event (widgets.OptionList.OptionSelected): The selection.
    """
    event.stop()
    question = self._question()
    if question is None:
        return
    self._choose(question['options'][event.option_index]['label'])

on_input_changed

on_input_changed(event)

Keeps the Other answer; for a single choice it replaces the picked option.

Parameters:

Name Type Description Default
event Changed

The change.

required
Source code in src/trading_agent/terminal/widgets/question_card.py
def on_input_changed(self, event: widgets.Input.Changed) -> None:
    """Keeps the Other answer; for a single choice it replaces the picked option.

    Args:
        event (widgets.Input.Changed): The change.
    """
    event.stop()
    question = self._question()
    if question is None:
        return
    draft = self._drafts[self._tab]
    draft['other'] = event.value
    if event.value.strip() and not question.get('multi_select'):
        draft['selected'] = []
    self._show_options()
    self._show_tabs()

on_input_submitted

on_input_submitted(event)

Moves on, or submits, after an Other answer.

Parameters:

Name Type Description Default
event Submitted

The submission.

required
Source code in src/trading_agent/terminal/widgets/question_card.py
def on_input_submitted(self, event: widgets.Input.Submitted) -> None:
    """Moves on, or submits, after an Other answer.

    Args:
        event (widgets.Input.Submitted): The submission.
    """
    event.stop()
    if not event.value.strip():
        return
    if len(self._questions) == 1:
        self.action_submit()
        return
    self.action_next_question()

action_next_question

action_next_question()

Shows the next question, or the review tab after the last.

Source code in src/trading_agent/terminal/widgets/question_card.py
def action_next_question(self) -> None:
    """Shows the next question, or the review tab after the last."""
    if len(self._questions) < 2:
        return
    self._tab = min(self._tab + 1, len(self._questions))
    self._show()
    self.focus_card()

action_previous_question

action_previous_question()

Shows the previous question.

Source code in src/trading_agent/terminal/widgets/question_card.py
def action_previous_question(self) -> None:
    """Shows the previous question."""
    if len(self._questions) < 2:
        return
    self._tab = max(self._tab - 1, 0)
    self._show()
    self.focus_card()

action_submit

action_submit()

Sends the answers, when every question has one.

Source code in src/trading_agent/terminal/widgets/question_card.py
def action_submit(self) -> None:
    """Sends the answers, when every question has one."""
    for index in range(len(self._questions)):
        if not self._is_answered(index):
            self._error('Answer every question first.')
            return
    self._send_answers()

action_dismiss_request

action_dismiss_request()

Dismisses the questions without answering; the desk uses its own judgment.

Source code in src/trading_agent/terminal/widgets/question_card.py
def action_dismiss_request(self) -> None:
    """Dismisses the questions without answering; the desk uses its own judgment."""
    self._dismiss()