listpick 0.1.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- list_picker/__init__.py +0 -0
- list_picker/list_picker_app.py +2076 -0
- list_picker/ui/__init__.py +0 -0
- list_picker/ui/help_screen.py +91 -0
- list_picker/ui/input_field.py +165 -0
- list_picker/ui/keys.py +228 -0
- list_picker/ui/list_picker_colours.py +258 -0
- list_picker/utils/__init__.py +0 -0
- list_picker/utils/clipboard_operations.py +33 -0
- list_picker/utils/dump.py +78 -0
- list_picker/utils/filtering.py +32 -0
- list_picker/utils/generate_data.py +74 -0
- list_picker/utils/options_selectors.py +67 -0
- list_picker/utils/search_and_filter_utils.py +79 -0
- list_picker/utils/searching.py +76 -0
- list_picker/utils/sorting.py +120 -0
- list_picker/utils/table_to_list_of_lists.py +188 -0
- list_picker/utils/utils.py +251 -0
- listpick-0.1.4.0.dist-info/METADATA +186 -0
- listpick-0.1.4.0.dist-info/RECORD +22 -0
- listpick-0.1.4.0.dist-info/WHEEL +5 -0
- listpick-0.1.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,258 @@
|
|
1
|
+
#!/bin/python
|
2
|
+
import curses
|
3
|
+
|
4
|
+
"""
|
5
|
+
Define colour options for list_picker, help, and notification
|
6
|
+
"""
|
7
|
+
def get_theme_count() -> int:
|
8
|
+
col_list = []
|
9
|
+
i = 0
|
10
|
+
for i in range(100):
|
11
|
+
x = get_colours(i)
|
12
|
+
if x in col_list:
|
13
|
+
break
|
14
|
+
col_list.append(x)
|
15
|
+
return i
|
16
|
+
|
17
|
+
|
18
|
+
def get_colours(pick:int=0) -> dict[str, int]:
|
19
|
+
""" Define colour options for list_picker. """
|
20
|
+
colours = [
|
21
|
+
### Green header, green title, green modes, purple selected, blue cursor
|
22
|
+
{
|
23
|
+
'background': 232,
|
24
|
+
'normal_fg': 253,
|
25
|
+
'unselected_bg': 232,
|
26
|
+
'unselected_fg': 253,
|
27
|
+
'cursor_bg': 25,
|
28
|
+
'cursor_fg': 253,
|
29
|
+
'selected_bg': 135,
|
30
|
+
'selected_fg': 253,
|
31
|
+
'header_bg': 253,
|
32
|
+
'header_fg': 232,
|
33
|
+
'error_bg': 232,
|
34
|
+
'error_fg': curses.COLOR_RED,
|
35
|
+
'complete_bg': 232,
|
36
|
+
'complete_fg': 82,
|
37
|
+
'waiting_bg': 232,
|
38
|
+
'waiting_fg': curses.COLOR_YELLOW,
|
39
|
+
'active_bg': 232,
|
40
|
+
'active_fg': 33,
|
41
|
+
'paused_bg': 232,
|
42
|
+
'paused_fg': 244,
|
43
|
+
'search_bg': 162,
|
44
|
+
'search_fg': 253,
|
45
|
+
'active_input_bg': 253,
|
46
|
+
'active_input_fg': 28,
|
47
|
+
'modes_selected_bg': 28,
|
48
|
+
'modes_selected_fg': 253,
|
49
|
+
'modes_unselected_bg': 253,
|
50
|
+
'modes_unselected_fg': 232,
|
51
|
+
'title_bar': 28,
|
52
|
+
'title_bg': 28,
|
53
|
+
'title_fg': 253,
|
54
|
+
'scroll_bar_bg': 247,
|
55
|
+
'selected_header_column_bg': 247,
|
56
|
+
'selected_header_column_fg': 232,
|
57
|
+
'footer_bg': 28,
|
58
|
+
'footer_fg': 253,
|
59
|
+
'refreshing_bg': 28,
|
60
|
+
'refreshing_fg': 253,
|
61
|
+
'refreshing_inactive_bg': 28,
|
62
|
+
'refreshing_inactive_fg': 232,
|
63
|
+
'40pc_bg': 232,
|
64
|
+
'40pc_fg': 166,
|
65
|
+
'footer_string_bg': 28,
|
66
|
+
'footer_string_fg': 253,
|
67
|
+
},
|
68
|
+
### Black and white
|
69
|
+
{
|
70
|
+
'background': 232,
|
71
|
+
'normal_fg': 253,
|
72
|
+
'unselected_bg': 232,
|
73
|
+
'unselected_fg': 253,
|
74
|
+
'cursor_bg': 253,
|
75
|
+
'cursor_fg': 232,
|
76
|
+
'selected_bg': 253,
|
77
|
+
'selected_fg': 232,
|
78
|
+
'header_bg': 253,
|
79
|
+
'header_fg': 232,
|
80
|
+
'error_bg': 232,
|
81
|
+
'error_fg': 253,
|
82
|
+
'complete_bg': 232,
|
83
|
+
'complete_fg': 253,
|
84
|
+
'waiting_bg': 232,
|
85
|
+
'waiting_fg': 253,
|
86
|
+
'active_bg': 232,
|
87
|
+
'active_fg': 253,
|
88
|
+
'paused_bg': 232,
|
89
|
+
'paused_fg': 253,
|
90
|
+
'search_bg': 253,
|
91
|
+
'search_fg': 232,
|
92
|
+
'active_input_bg': 232,
|
93
|
+
'active_input_fg': 253,
|
94
|
+
'modes_selected_bg': 253,
|
95
|
+
'modes_selected_fg': 232,
|
96
|
+
'modes_unselected_bg': 232,
|
97
|
+
'modes_unselected_fg': 253,
|
98
|
+
'title_bar': 232,
|
99
|
+
'title_bg': 253,
|
100
|
+
'title_fg': 232,
|
101
|
+
'scroll_bar_bg': 253,
|
102
|
+
'selected_header_column_bg': 232,
|
103
|
+
'selected_header_column_fg': 253,
|
104
|
+
'footer_bg': 253,
|
105
|
+
'footer_fg': 232,
|
106
|
+
'refreshing_bg': 253,
|
107
|
+
'refreshing_fg': 232,
|
108
|
+
'refreshing_inactive_bg': 232,
|
109
|
+
'refreshing_inactive_fg': 253,
|
110
|
+
'40pc_bg': 232,
|
111
|
+
'40pc_fg': 253,
|
112
|
+
'footer_string_bg': 253,
|
113
|
+
'footer_string_fg': 232,
|
114
|
+
},
|
115
|
+
### Blue header, blue title, blue modes, purple selected, green cursor
|
116
|
+
{
|
117
|
+
'background': 232,
|
118
|
+
'normal_fg': 253,
|
119
|
+
'unselected_bg': 232,
|
120
|
+
'unselected_fg': 253,
|
121
|
+
'cursor_bg': 28,
|
122
|
+
'cursor_fg': 253,
|
123
|
+
'selected_bg': 135,
|
124
|
+
'selected_fg': 253,
|
125
|
+
'header_bg': 253,
|
126
|
+
'header_fg': 232,
|
127
|
+
'error_bg': 232,
|
128
|
+
'error_fg': curses.COLOR_RED,
|
129
|
+
'complete_bg': 232,
|
130
|
+
'complete_fg': 82,
|
131
|
+
'waiting_bg': 232,
|
132
|
+
'waiting_fg': curses.COLOR_YELLOW,
|
133
|
+
'active_bg': 232,
|
134
|
+
'active_fg': 33,
|
135
|
+
'paused_bg': 232,
|
136
|
+
'paused_fg': 244,
|
137
|
+
'search_bg': 162,
|
138
|
+
'search_fg': 253,
|
139
|
+
'active_input_bg': 253,
|
140
|
+
'active_input_fg': 25,
|
141
|
+
'modes_selected_bg': 25,
|
142
|
+
'modes_selected_fg': 253,
|
143
|
+
'modes_unselected_bg': 253,
|
144
|
+
'modes_unselected_fg': 232,
|
145
|
+
'title_bar': 25,
|
146
|
+
'title_bg': 25,
|
147
|
+
'title_fg': 253,
|
148
|
+
'scroll_bar_bg': 247,
|
149
|
+
'selected_header_column_bg': 247,
|
150
|
+
'selected_header_column_fg': 232,
|
151
|
+
'footer_bg': 25,
|
152
|
+
'footer_fg': 253,
|
153
|
+
'refreshing_bg': 25,
|
154
|
+
'refreshing_fg': 253,
|
155
|
+
'refreshing_inactive_bg': 25,
|
156
|
+
'refreshing_inactive_fg': 232,
|
157
|
+
'40pc_bg': 232,
|
158
|
+
'40pc_fg': 166,
|
159
|
+
'footer_string_bg': 25,
|
160
|
+
'footer_string_fg': 253,
|
161
|
+
},
|
162
|
+
### Purple header, purple title, white modes, green selected, blue cursor
|
163
|
+
{
|
164
|
+
'background': 232,
|
165
|
+
'normal_fg': 253,
|
166
|
+
'unselected_bg': 232,
|
167
|
+
'unselected_fg': 253,
|
168
|
+
'cursor_bg': 57,
|
169
|
+
'cursor_fg': 253,
|
170
|
+
'selected_bg': 135,
|
171
|
+
'selected_fg': 253,
|
172
|
+
'header_bg': 253,
|
173
|
+
'header_fg': 232,
|
174
|
+
'error_bg': 232,
|
175
|
+
'error_fg': curses.COLOR_RED,
|
176
|
+
'complete_bg': 232,
|
177
|
+
'complete_fg': 82,
|
178
|
+
'waiting_bg': 232,
|
179
|
+
'waiting_fg': curses.COLOR_YELLOW,
|
180
|
+
'active_bg': 232,
|
181
|
+
'active_fg': 33,
|
182
|
+
'paused_bg': 232,
|
183
|
+
'paused_fg': 244,
|
184
|
+
'search_bg': 162,
|
185
|
+
'search_fg': 253,
|
186
|
+
'active_input_bg': 253,
|
187
|
+
'active_input_fg': 57,
|
188
|
+
'modes_selected_bg': 57,
|
189
|
+
'modes_selected_fg': 253,
|
190
|
+
'modes_unselected_bg': 253,
|
191
|
+
'modes_unselected_fg': 232,
|
192
|
+
'title_bar': 57,
|
193
|
+
'title_bg': 57,
|
194
|
+
'title_fg': 253,
|
195
|
+
'scroll_bar_bg': 247,
|
196
|
+
'selected_header_column_bg': 247,
|
197
|
+
'selected_header_column_fg': 232,
|
198
|
+
'footer_bg': 57,
|
199
|
+
'footer_fg': 253,
|
200
|
+
'refreshing_bg': 57,
|
201
|
+
'refreshing_fg': 253,
|
202
|
+
'refreshing_inactive_bg': 57,
|
203
|
+
'refreshing_inactive_fg': 232,
|
204
|
+
'40pc_bg': 232,
|
205
|
+
'40pc_fg': 166,
|
206
|
+
'footer_string_bg': 57,
|
207
|
+
'footer_string_fg': 253,
|
208
|
+
},
|
209
|
+
]
|
210
|
+
for colour in colours:
|
211
|
+
colour["20pc_bg"] = colour["background"]
|
212
|
+
colour["40pc_bg"] = colour["background"]
|
213
|
+
colour["60pc_bg"] = colour["background"]
|
214
|
+
colour["80pc_bg"] = colour["background"]
|
215
|
+
colour["100pc_bg"] = colour["background"]
|
216
|
+
colour["error_bg"] = colour["background"]
|
217
|
+
colour["complete_bg"] = colour["background"]
|
218
|
+
colour["waiting_bg"] = colour["background"]
|
219
|
+
colour["active_bg"] = colour["background"]
|
220
|
+
colour["paused_bg"] = colour["background"]
|
221
|
+
colour["paused_bg"] = colour["background"]
|
222
|
+
# colour["search_bg"] = colour["background"]
|
223
|
+
if pick > len(colours) - 1:
|
224
|
+
return colours[0]
|
225
|
+
return colours[pick]
|
226
|
+
|
227
|
+
def get_help_colours(pick: int=0) -> dict:
|
228
|
+
colours = [get_colours(i) for i in range(get_theme_count())]
|
229
|
+
for i in range(len(colours)):
|
230
|
+
colours[i]['cursor_bg'] = 235
|
231
|
+
colours[i]['cursor_fg'] = 253
|
232
|
+
colours[i]['selected_bg'] = 25
|
233
|
+
colours[i]['selected_fg'] = 253
|
234
|
+
|
235
|
+
if pick > len(colours) - 1:
|
236
|
+
return colours[0]
|
237
|
+
return colours[pick]
|
238
|
+
|
239
|
+
|
240
|
+
def get_notification_colours(pick:int=0) -> dict:
|
241
|
+
colours = [get_colours(i) for i in range(get_theme_count())]
|
242
|
+
for i in range(len(colours)):
|
243
|
+
colours[i]['background'] = 237
|
244
|
+
colours[i]['unselected_bg'] = 237
|
245
|
+
colours[i]['cursor_bg'] = 237
|
246
|
+
colours[i]['selected_bg'] = 237
|
247
|
+
|
248
|
+
# Black and white
|
249
|
+
colours[1]['background'] = 237
|
250
|
+
colours[1]['unselected_bg'] = 237
|
251
|
+
colours[1]['cursor_bg'] = 237
|
252
|
+
colours[1]['cursor_fg'] = 253
|
253
|
+
colours[1]['selected_bg'] = 237
|
254
|
+
colours[1]['selected_fg'] = 253
|
255
|
+
|
256
|
+
if pick > len(colours) - 1:
|
257
|
+
return colours[0]
|
258
|
+
return colours[pick]
|
File without changes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/bin/python
|
2
|
+
import pyperclip
|
3
|
+
from typing import Tuple
|
4
|
+
|
5
|
+
"""
|
6
|
+
representation: python, tab-separated, comma-separated, current view
|
7
|
+
rows: selected, all, filtered
|
8
|
+
columns: all, not hidden
|
9
|
+
"""
|
10
|
+
|
11
|
+
def copy_to_clipboard(items: list[list[str]], indexed_items: list[Tuple[int, list[str]]], selections: dict, hidden_columns: set, representation: str="python", copy_hidden_cols: bool = False, separator="\t") -> None:
|
12
|
+
"""
|
13
|
+
Copy selected items to clipboard.
|
14
|
+
|
15
|
+
representation (str): The representation of the rows that should be copied.
|
16
|
+
accepted values: python, csv, tsv, current view, custom_sv
|
17
|
+
|
18
|
+
"""
|
19
|
+
selected_indices = [i for i, selected in selections.items() if selected]
|
20
|
+
rows_to_copy = [item for i, item in enumerate(items) if i in selected_indices]
|
21
|
+
formatted_items = [[cell for i, cell in enumerate(item) if i not in hidden_columns or copy_hidden_cols] for item in rows_to_copy]
|
22
|
+
if representation == "python":
|
23
|
+
pyperclip.copy(repr(formatted_items))
|
24
|
+
elif representation == "tsv":
|
25
|
+
pyperclip.copy("\n".join(["\t".join(row) for row in formatted_items]))
|
26
|
+
elif representation == "csv":
|
27
|
+
pyperclip.copy("\n".join([",".join(row) for row in formatted_items]))
|
28
|
+
elif representation == "custom_sv":
|
29
|
+
# Ensure that escapes are interpreted properly in separator
|
30
|
+
separator = bytes(separator, "utf-8").decode("unicode_escape")
|
31
|
+
pyperclip.copy("\n".join([separator.join(row) for row in formatted_items]))
|
32
|
+
# elif representation == "current_view":
|
33
|
+
# pyperclip.copy("\n".join([format_row(row, hidden_columns, column_widths, separator, centre_in_cols) for row in formatted_items]))
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/bin/python
|
2
|
+
import os
|
3
|
+
import dill as pickle
|
4
|
+
|
5
|
+
def make_list_unique(l:list) -> list:
|
6
|
+
result = []
|
7
|
+
for i in l:
|
8
|
+
if i not in result:
|
9
|
+
result.append(i)
|
10
|
+
else:
|
11
|
+
result[-1] += f'_({len(result)-1})'
|
12
|
+
result.append(i)
|
13
|
+
return result
|
14
|
+
|
15
|
+
def dump_state(function_data:dict, file_path:str) -> None:
|
16
|
+
""" Dump state of list picker to file. """
|
17
|
+
exclude_keys = ["refresh_function", "get_data_startup", "get_new_data", "auto_refresh"]
|
18
|
+
function_data = {key: val for key, val in function_data.items() if key not in exclude_keys}
|
19
|
+
with open(os.path.expandvars(os.path.expanduser(file_path)), 'wb') as f:
|
20
|
+
pickle.dump(function_data, f)
|
21
|
+
|
22
|
+
def dump_data(function_data:dict, file_path:str, format="pickle") -> str:
|
23
|
+
""" Dump data from list_picker. Returns whether there was an error. """
|
24
|
+
include_keys = ["items", "header"]
|
25
|
+
function_data = {key: val for key, val in function_data.items() if key in include_keys }
|
26
|
+
|
27
|
+
try:
|
28
|
+
if format == "pickle":
|
29
|
+
with open(os.path.expandvars(os.path.expanduser(file_path)), 'wb') as f:
|
30
|
+
pickle.dump(function_data, f)
|
31
|
+
elif format == "csv":
|
32
|
+
import csv
|
33
|
+
with open(os.path.expandvars(os.path.expanduser(file_path)), mode='w', newline='') as f:
|
34
|
+
writer = csv.writer(f)
|
35
|
+
for row in [function_data["header"]] + function_data["items"]:
|
36
|
+
writer.writerow(row)
|
37
|
+
elif format == "tsv":
|
38
|
+
import csv
|
39
|
+
with open(os.path.expandvars(os.path.expanduser(file_path)), mode='w', newline='') as f:
|
40
|
+
writer = csv.writer(f, delimiter='\t')
|
41
|
+
for row in [function_data["header"]] + function_data["items"]:
|
42
|
+
writer.writerow(row)
|
43
|
+
|
44
|
+
elif format == "json":
|
45
|
+
import json
|
46
|
+
with open(os.path.expandvars(os.path.expanduser(file_path)), mode='w') as f:
|
47
|
+
json.dump([function_data["header"]]+ function_data["items"], f, indent=4)
|
48
|
+
|
49
|
+
elif format == "feather":
|
50
|
+
import pyarrow as pa
|
51
|
+
import pandas as pd
|
52
|
+
import pyarrow.feather as feather
|
53
|
+
table = pa.Table.from_pandas(pd.DataFrame(function_data["items"], columns=make_list_unique(function_data["header"])))
|
54
|
+
feather.write_feather(table, os.path.expandvars(os.path.expanduser(file_path)))
|
55
|
+
|
56
|
+
elif format == "parquet":
|
57
|
+
import pyarrow as pa
|
58
|
+
import pyarrow.parquet as pq
|
59
|
+
import pandas as pd
|
60
|
+
table = pa.Table.from_pandas(pd.DataFrame(function_data["items"], columns=make_list_unique(function_data["header"])))
|
61
|
+
|
62
|
+
pq.write_table(table, os.path.expandvars(os.path.expanduser(file_path)))
|
63
|
+
elif format == "msgpack":
|
64
|
+
import msgpack as mp
|
65
|
+
with open(os.path.expandvars(os.path.expanduser(file_path)), mode='wb') as f:
|
66
|
+
mp.dump([function_data["header"]]+ function_data["items"], f)
|
67
|
+
except Exception as e:
|
68
|
+
return str(e)
|
69
|
+
return ""
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
def load_state(file_path:str) -> dict:
|
74
|
+
""" Load list_picker state from dump. """
|
75
|
+
with open(os.path.expandvars(os.path.expanduser(file_path)), 'rb') as f:
|
76
|
+
loaded_data = pickle.load(f)
|
77
|
+
return loaded_data
|
78
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/bin/python
|
2
|
+
import re
|
3
|
+
from typing import Tuple
|
4
|
+
from list_picker.utils.search_and_filter_utils import apply_filter, tokenise
|
5
|
+
import os
|
6
|
+
|
7
|
+
def filter_items(items: list[list[str]], indexed_items: list[Tuple[int, list[str]]], query: str) -> list[Tuple[int, list[str]]]:
|
8
|
+
"""
|
9
|
+
Filter items based on the query.
|
10
|
+
|
11
|
+
Accepts:
|
12
|
+
regular expressions
|
13
|
+
--# to specify column to match
|
14
|
+
--i to specify case-sensitivity (it is case insensitive by default)
|
15
|
+
--v to specify inverse match
|
16
|
+
|
17
|
+
E.g.,
|
18
|
+
|
19
|
+
--1 query matches query in the 1 column
|
20
|
+
|
21
|
+
|
22
|
+
Returns indexed_items, which is a list of tuples; each tuple consists of the index and the data of the matching row in the original items list.
|
23
|
+
"""
|
24
|
+
|
25
|
+
|
26
|
+
invert_filter = False
|
27
|
+
case_sensitive = False
|
28
|
+
|
29
|
+
filters = tokenise(query)
|
30
|
+
|
31
|
+
indexed_items = [(i, item) for i, item in enumerate(items) if apply_filter(item, filters)]
|
32
|
+
return indexed_items
|
@@ -0,0 +1,74 @@
|
|
1
|
+
import subprocess
|
2
|
+
import os
|
3
|
+
from typing import Tuple, Callable
|
4
|
+
import toml
|
5
|
+
|
6
|
+
def generate_columns(funcs: list, files: list) -> list[list[str]]:
|
7
|
+
"""
|
8
|
+
Takes a list of functions and a list of files. Each function is run for each file and a list of lists is returned.
|
9
|
+
"""
|
10
|
+
items = []
|
11
|
+
for file in files:
|
12
|
+
item = []
|
13
|
+
for func in funcs:
|
14
|
+
item.append(func(file))
|
15
|
+
items.append(item)
|
16
|
+
|
17
|
+
return items
|
18
|
+
|
19
|
+
def command_to_func(command: str) -> Callable:
|
20
|
+
"""
|
21
|
+
Convert a command string to a function that will run the command.
|
22
|
+
|
23
|
+
E.g.,
|
24
|
+
mediainfo {} | grep -i format
|
25
|
+
mediainfo {} | grep -i format | head -n 1 | awk '{{print $3}}'
|
26
|
+
"""
|
27
|
+
|
28
|
+
func = lambda arg: subprocess.run(command.format(repr(arg)), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.decode("utf-8")
|
29
|
+
return func
|
30
|
+
|
31
|
+
def load_environment(envs:dict):
|
32
|
+
"""
|
33
|
+
Load environment variables from an envs dict.
|
34
|
+
"""
|
35
|
+
|
36
|
+
if "cwd" in envs:
|
37
|
+
os.chdir(os.path.expandvars(os.path.expanduser(envs["cwd"])))
|
38
|
+
|
39
|
+
|
40
|
+
def read_toml(file_path) -> Tuple[dict, list, list]:
|
41
|
+
"""
|
42
|
+
Read toml file and return the environment, commands and header sections.
|
43
|
+
"""
|
44
|
+
with open(file_path, 'r') as file:
|
45
|
+
config = toml.load(file)
|
46
|
+
|
47
|
+
environment = config['environment'] if 'environment' in config else {}
|
48
|
+
data = config['data'] if 'data' in config else {}
|
49
|
+
commands = [command.strip() for command in data['commands']] if 'commands' in data else []
|
50
|
+
header = [header for header in data['header']] if 'header' in data else []
|
51
|
+
return environment, commands, header
|
52
|
+
|
53
|
+
def generate_list_picker_data(file_path: str) -> Tuple[list[list[str]], list[str]]:
|
54
|
+
"""
|
55
|
+
Generate data for list picker based upon the toml file commands.
|
56
|
+
"""
|
57
|
+
environment, commands, header = read_toml(file_path)
|
58
|
+
lines = commands
|
59
|
+
|
60
|
+
if environment:
|
61
|
+
load_environment(environment)
|
62
|
+
|
63
|
+
arg_command = lines[0]
|
64
|
+
args = subprocess.run(arg_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.decode("utf-8").split("\n")
|
65
|
+
args = [arg.strip() for arg in args if arg]
|
66
|
+
|
67
|
+
commands_list = [line.strip() for line in lines[1:]]
|
68
|
+
command_funcs = [command_to_func(command) for command in commands_list]
|
69
|
+
items = generate_columns(command_funcs, args)
|
70
|
+
items = [[args[i]] + items[i] for i in range(len(args))]
|
71
|
+
items = [[cell.strip() for cell in item] for item in items]
|
72
|
+
|
73
|
+
return items, header
|
74
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/bin/python
|
2
|
+
import curses
|
3
|
+
from typing import Tuple
|
4
|
+
from list_picker.ui.input_field import input_field
|
5
|
+
from list_picker.utils.utils import dir_picker
|
6
|
+
|
7
|
+
def default_option_input(stdscr: curses.window, refresh_screen_function=None, starting_value:str="", field_name:str="Opts", registers={}) -> Tuple[bool, str]:
|
8
|
+
# notification(stdscr, message=f"opt required for {index}")
|
9
|
+
usrtxt = f"{starting_value} " if starting_value else ""
|
10
|
+
h, w = stdscr.getmaxyx()
|
11
|
+
# field_end = w-38 if show_footer else w-3
|
12
|
+
field_end = w-3
|
13
|
+
field_end_f = lambda: stdscr.getmaxyx()[1]-3
|
14
|
+
usrtxt, return_val = input_field(
|
15
|
+
stdscr,
|
16
|
+
usrtxt=usrtxt,
|
17
|
+
field_name=field_name,
|
18
|
+
x=lambda:2,
|
19
|
+
y=lambda: stdscr.getmaxyx()[0]-1,
|
20
|
+
max_length=field_end_f,
|
21
|
+
registers=registers,
|
22
|
+
)
|
23
|
+
if return_val: return True, usrtxt
|
24
|
+
else: return False, starting_value
|
25
|
+
|
26
|
+
|
27
|
+
def default_option_selector(stdscr: curses.window, refresh_screen_function=None, starting_value:str="", field_name:str="Opts", registers={}) -> Tuple[bool, str]:
|
28
|
+
# notification(stdscr, message=f"opt required for {index}")
|
29
|
+
usrtxt = f"{starting_value} " if starting_value else ""
|
30
|
+
h, w = stdscr.getmaxyx()
|
31
|
+
# field_end = w-38 if show_footer else w-3
|
32
|
+
field_end = w-3
|
33
|
+
field_end_f = lambda: stdscr.getmaxyx()[1]-3
|
34
|
+
usrtxt, return_val = input_field(
|
35
|
+
stdscr,
|
36
|
+
usrtxt=usrtxt,
|
37
|
+
field_name=field_name,
|
38
|
+
x=lambda:2,
|
39
|
+
y=lambda: stdscr.getmaxyx()[0]-1,
|
40
|
+
max_length=field_end_f,
|
41
|
+
registers=registers,
|
42
|
+
)
|
43
|
+
if return_val: return True, usrtxt
|
44
|
+
else: return False, starting_value
|
45
|
+
|
46
|
+
|
47
|
+
def output_file_option_selector(stdscr:curses.window, refresh_screen_function, registers={}) -> Tuple[bool, str]:
|
48
|
+
s = dir_picker()
|
49
|
+
|
50
|
+
stdscr.clear()
|
51
|
+
stdscr.refresh()
|
52
|
+
refresh_screen_function()
|
53
|
+
usrtxt = f"{s}/"
|
54
|
+
h, w = stdscr.getmaxyx()
|
55
|
+
# field_end = w-38 if show_footer else w-3
|
56
|
+
field_end_f = lambda: stdscr.getmaxyx()[1]-3
|
57
|
+
usrtxt, return_val = input_field(
|
58
|
+
stdscr,
|
59
|
+
usrtxt=usrtxt,
|
60
|
+
field_name="Save as",
|
61
|
+
x=lambda:2,
|
62
|
+
y=lambda: stdscr.getmaxyx()[0]-1,
|
63
|
+
max_length=field_end_f,
|
64
|
+
registers=registers,
|
65
|
+
)
|
66
|
+
if return_val: return True, usrtxt
|
67
|
+
else: return False, ""
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import re
|
2
|
+
|
3
|
+
def apply_filter(row: list[str], filters: dict, case_sensitive: bool = False, add_highlights:bool = False, highlights: list=[]) -> bool:
|
4
|
+
""" Checks if row matches the filter. """
|
5
|
+
for col, filter_list in filters.items():
|
6
|
+
for filter in filter_list:
|
7
|
+
if case_sensitive or (filter != filter.lower()):
|
8
|
+
pattern = re.compile(filter)
|
9
|
+
else:
|
10
|
+
pattern = re.compile(filter, re.IGNORECASE)
|
11
|
+
if col == -1: # Apply filter to all columns
|
12
|
+
if not any(pattern.search(str(item)) for item in row):
|
13
|
+
return False
|
14
|
+
# return not invert_filter
|
15
|
+
elif col >= len(row) or col < 0:
|
16
|
+
return False
|
17
|
+
else:
|
18
|
+
cell_value = str(row[col])
|
19
|
+
if not pattern.search(str(cell_value)):
|
20
|
+
return False
|
21
|
+
# return invert_filter
|
22
|
+
|
23
|
+
if add_highlights:
|
24
|
+
for col, filter_list in filters.items():
|
25
|
+
for filter in filter_list:
|
26
|
+
hcol = "all" if col == -1 else col
|
27
|
+
highlights.append({
|
28
|
+
"match": filter,
|
29
|
+
"field": hcol,
|
30
|
+
"color": 10,
|
31
|
+
"type": "search",
|
32
|
+
})
|
33
|
+
|
34
|
+
return True
|
35
|
+
|
36
|
+
|
37
|
+
def tokenise(query:str) -> dict:
|
38
|
+
""" Convert query into dict consisting of filters. '--1 """
|
39
|
+
filters = {}
|
40
|
+
|
41
|
+
# tokens = re.split(r'(\s+--\d+|\s+--i)', query)
|
42
|
+
# tokens = re.split(r'((\s+|^)--\w)', query)
|
43
|
+
tokens = re.split(r'\s+', query)
|
44
|
+
tokens = [token.strip() for token in tokens if token.strip()] # Remove empty tokens
|
45
|
+
i = 0
|
46
|
+
while i < len(tokens):
|
47
|
+
token = tokens[i]
|
48
|
+
if token:
|
49
|
+
if token.startswith("--"):
|
50
|
+
flag = token
|
51
|
+
if flag == '--v':
|
52
|
+
invert_filter = True
|
53
|
+
i += 1
|
54
|
+
elif flag == '--i':
|
55
|
+
case_sensitive = True
|
56
|
+
i += 1
|
57
|
+
else:
|
58
|
+
if i+1 >= len(tokens):
|
59
|
+
break
|
60
|
+
col = int(flag[2:])
|
61
|
+
arg = tokens[i+1].strip()
|
62
|
+
try:
|
63
|
+
i+=2
|
64
|
+
re.compile(arg)
|
65
|
+
if col in filters: filters[col].append(arg)
|
66
|
+
else: filters[col] = [arg]
|
67
|
+
except:
|
68
|
+
pass
|
69
|
+
else:
|
70
|
+
try:
|
71
|
+
i += 1
|
72
|
+
re.compile(token)
|
73
|
+
if -1 in filters: filters[-1].append(token)
|
74
|
+
else: filters[-1] = [token]
|
75
|
+
except:
|
76
|
+
pass
|
77
|
+
else:
|
78
|
+
i += 1
|
79
|
+
return filters
|