Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion ItsPrompt/data/table.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import os
from typing import TYPE_CHECKING, Union

from .type import TablePromptDict, TablePromptList
from ..objects.table.table_base import TableDataBase
from ..objects.table.table_dict import TableDataFromDict
from ..objects.table.table_list import TableDataFromList

# only import pandas and TableData if pandas is installed
try:
Expand All @@ -19,7 +21,7 @@

class Table:

def __init__(self, data: Union["DataFrame", dict[str, list[str]]]) -> None:
def __init__(self, data: Union["DataFrame", TablePromptDict, TablePromptList]) -> None:
"""
Creates a table object for storing the drawable table instance

Expand All @@ -31,6 +33,8 @@ def __init__(self, data: Union["DataFrame", dict[str, list[str]]]) -> None:
self.data: TableDataBase
if type(data) is dict:
self.data = TableDataFromDict(data)
elif type(data) is list:
self.data = TableDataFromList(data)
elif type(data) is DataFrame:
self.data = TableDataFromDF(data)

Expand Down
3 changes: 3 additions & 0 deletions ItsPrompt/data/type.py
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
CompletionDict = dict[str, "CompletionDict | None"]

TablePromptDict = dict[str, list[str]]
TablePromptList = list[list[str]]
5 changes: 3 additions & 2 deletions ItsPrompt/objects/table/table_dict.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from typing import Generator

from .table_base import TableDataBase
from ...data.type import TablePromptDict


class TableDataFromDict(TableDataBase):

def __init__(self, data: dict[str, list[str]]) -> None:
def __init__(self, data: TablePromptDict) -> None:
# make sure every list has same length
lengths = set([len(t) for t in data.values()])
if len(lengths) != 1:
Expand Down Expand Up @@ -52,5 +53,5 @@ def del_key(self, row: int, col: int):
def get_column_location(self, val: str) -> int:
return self.columns.index(val)

def get_data(self) -> dict[str, list[str]]:
def get_data(self) -> TablePromptDict:
return self.data
17 changes: 17 additions & 0 deletions ItsPrompt/objects/table/table_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from .table_dict import TableDataFromDict
from ...data.type import TablePromptDict, TablePromptList


class TableDataFromList(TableDataFromDict):

def __init__(self, data: TablePromptList):
# convert list to dict
dict_data: TablePromptDict = {f"{col}": rows for col, rows in enumerate(data)}

super().__init__(dict_data)

def get_data(self) -> TablePromptList: # type: ignore
# convert dict to list
list_data: TablePromptList = list(self.data.values())

return list_data
10 changes: 5 additions & 5 deletions ItsPrompt/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
)

from .data.style import PromptStyle, convert_style, default_style
from .data.type import CompletionDict
from .data.type import CompletionDict, TablePromptDict, TablePromptList
from .keyboard_handler import generate_key_bindings
from .prompts.checkbox import CheckboxPrompt
from .prompts.confirm import ConfirmPrompt
Expand Down Expand Up @@ -473,22 +473,22 @@ def input(
def table(
cls,
question: str,
data: Union["DataFrame", dict[str, list[str]]],
data: Union["DataFrame", TablePromptDict, TablePromptList],
style: PromptStyle | None = None,
) -> Union["DataFrame", dict[str, list[str]]]:
) -> Union["DataFrame", TablePromptDict, TablePromptList]:
"""
Ask the user for filling out the displayed table.

This method shows the question alongside a table, which the user may navigate with the arrow keys. The user
has the ability to use the up, down and enter keys to navigate between the options and change the text in
each cell.

The `data` is either a pandas DataFrame or a dictionary.
The `data` is either a pandas DataFrame, a list or a dictionary (more in the README.md).

:param question: The question to display
:type question: str
:param data: The data to display
:type data: DataFrame | dict[str, list[str]]
:type data: DataFrame | TablePromptDict | TablePromptList
:param style: A separate style to style the prompt (empty or None for default style), defaults to None
:type style: PromptStyle | None, optional
:raises KeyboardInterrupt: When the user presses ctrl-c, `KeyboardInterrupt` will be raised
Expand Down
7 changes: 4 additions & 3 deletions ItsPrompt/prompts/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from prompt_toolkit.layout.controls import FormattedTextControl

from ..data.table import Table
from ..data.type import TablePromptDict, TablePromptList

if TYPE_CHECKING: # pragma: no cover
from pandas import DataFrame
Expand All @@ -16,7 +17,7 @@ class TablePrompt(Application):
def __init__(
self,
question: str,
data: Union["DataFrame", dict[str, list[str]]],
data: Union["DataFrame", TablePromptDict, TablePromptList],
*args,
**kwargs,
):
Expand Down Expand Up @@ -48,10 +49,10 @@ def update(self):

self.prompt_content.text = HTML(content)

def prompt(self) -> Union["DataFrame", dict[str, list[str]], None]:
def prompt(self) -> Union["DataFrame", TablePromptDict, None]:
"""start the application, returns the return value"""
self.update()
out: Union["DataFrame", dict[str, list[str]], None] = self.run()
out: Union["DataFrame", TablePromptDict, None] = self.run()

return out

Expand Down
86 changes: 81 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ Prompt.select(
)
```

Read more about the options attribute at [Options](#options).

*additional information on the function arguments can be found in the docstring*

### `raw_select`
Expand All @@ -154,6 +156,8 @@ Prompt.raw_select(
)
```

Read more about the options attribute at [Options](#options).

*additional information on the function arguments can be found in the docstring*

### `expand`
Expand All @@ -170,6 +174,8 @@ Prompt.expand(
)
```

Read more about the options attribute at [Options](#options).

*additional information on the function arguments can be found in the docstring*

### `checkbox`
Expand All @@ -187,6 +193,8 @@ Prompt.checkbox(
)
```

Read more about the options attribute at [Options](#options).

*additional information on the function arguments can be found in the docstring*

### `confirm`
Expand Down Expand Up @@ -221,20 +229,31 @@ Prompt.input(
)
```

Read more about the prompt validation and prompt completion attribute at [Prompt Validation](#prompt-validation)
and [Prompt Commpletion](#prompt-completion).

*additional information on the function arguments can be found in the docstring*

### `table`

![](https://raw.githubusercontent.com/TheItsProjects/ItsPrompt/main/media/table.png)

```py
# using a dictionary
Prompt.table(
question="something",
data={"0": ["something"]},
style=my_style,
)

# or, after having installed pandas
# using a list
Prompt.table(
question="something",
data=[["something"]],
style=my_style,
)

# or, after having installed pandas, using a DataFrame

Prompt.table(
question='something',
Expand All @@ -243,6 +262,8 @@ Prompt.table(
)
```

Read more about the data attribute at [TablePrompt Data](#tableprompt-data).

*additional information on the function arguments can be found in the docstring*

---
Expand All @@ -264,12 +285,67 @@ If an option is given as a `tuple`, the first value will be the options name, th

---

### Data
### TablePrompt Data

The `table` prompt takes a mandatory `data` argument, which needs to be either:

- a `TablePromptList`
- a `TablePromptDict`
- a `pandas.DataFrame` (NOTE: `pandas` needs to be installed!)

The `data` is used as the content of the table. The user may change the fields of the table. The output of the `table`
prompt is of the same type, as the input data is represented, with the user given values.

***TablePromptList***

This is a list of the type: `list[list[str]]`.

Every sub-list is a column, every item is a cell.

```py
[["field 1", "field 2"], ["field 3", "field 4"]]
```

will be rendered:

| 0 | 1 |
|---------|---------|
| field 1 | field 3 |
| field 2 | field 4 |

***TablePromptDict***

This is a dictionary of the type: `dict[str, list[str]]`.

Every key is a column name, every value is the column itself.

```py
{"column 1": ["field 1", "field 2"], "column 2": ["field 3", "field 4"]}
```

will be rendered:

| column 1 | column 2 |
|----------|----------|
| field 1 | field 3 |
| field 2 | field 4 |

***DataFrame***

To use `pandas.DataFrame`, you first need to install `pandas`.

```py
DataFrame(["field 1", "field 2"])
```

will be rendered:

The `table` prompt takes a mandatory `data` argument, which needs to be a `pandas.DataFrame`.
| 0 |
|---------|
| field 1 |
| field 2 |

This `DataFrame` is used as the content of the table. The user may change the fields of the table. The output of
the `table` prompt is a `pandas.DataFrame` with the user given values.
***Additional information***

Currently, the output will convert all input values to a `str`, so `int`, `bool`, ... will be converted to strings. This
is a current limitation of the way the table is displayed, but may later be updated.
Expand Down
34 changes: 33 additions & 1 deletion tests/test_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from prompt_toolkit.completion import FuzzyWordCompleter
from prompt_toolkit.keys import Keys

from ItsPrompt.data.type import TablePromptDict, TablePromptList
from ItsPrompt.prompt import Prompt


Expand Down Expand Up @@ -434,7 +435,7 @@ def test_table_dataframe(send_keys, mock_terminal_size, keys: list[Keys | str],
],
],
)
def test_table_dictionary(send_keys, mock_terminal_size, keys: list[Keys | str], a: dict[str, list[str]]):
def test_table_dictionary(send_keys, mock_terminal_size, keys: list[Keys | str], a: TablePromptDict):
data = {"0": ["first", "second", "third"]}

send_keys(*keys)
Expand All @@ -444,6 +445,37 @@ def test_table_dictionary(send_keys, mock_terminal_size, keys: list[Keys | str],
assert ans == a


@pytest.mark.parametrize(
"keys,a",
[
[
(Keys.Enter,),
[["first", "second", "third"]],
],
[
(Keys.Down, "new", Keys.Enter),
[["first", "secondnew", "third"]],
],
[
(Keys.Left, Keys.Right, Keys.Up, Keys.Down, Keys.Enter),
[["first", "second", "third"]],
],
[
(Keys.Backspace, Keys.Enter),
[["firs", "second", "third"]],
],
],
)
def test_table_list(send_keys, mock_terminal_size, keys: list[Keys | str], a: TablePromptList):
data = [["first", "second", "third"]]

send_keys(*keys)

ans = Prompt.table("", data)

assert ans == a


def test_table_raises_keyboard_interrupt(send_keys):
send_keys(Keys.ControlC)

Expand Down