visidata 2.11.dev0__py3-none-any.whl → 3.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.
- visidata/__init__.py +72 -91
- visidata/_input.py +263 -44
- visidata/_open.py +84 -29
- visidata/_types.py +22 -4
- visidata/_urlcache.py +17 -4
- visidata/aggregators.py +65 -25
- visidata/apps/__init__.py +0 -0
- visidata/apps/vdsql/__about__.py +8 -0
- visidata/apps/vdsql/__init__.py +5 -0
- visidata/apps/vdsql/__main__.py +27 -0
- visidata/apps/vdsql/_ibis.py +748 -0
- visidata/apps/vdsql/bigquery.py +61 -0
- visidata/apps/vdsql/clickhouse.py +53 -0
- visidata/apps/vdsql/setup.py +40 -0
- visidata/apps/vdsql/snowflake.py +67 -0
- visidata/apps/vgit/__init__.py +13 -0
- visidata/apps/vgit/__main__.py +3 -0
- visidata/apps/vgit/abort.py +23 -0
- visidata/apps/vgit/blame.py +76 -0
- visidata/apps/vgit/branch.py +153 -0
- visidata/apps/vgit/config.py +95 -0
- visidata/apps/vgit/diff.py +169 -0
- visidata/apps/vgit/gitsheet.py +161 -0
- visidata/apps/vgit/grep.py +37 -0
- visidata/apps/vgit/log.py +81 -0
- visidata/apps/vgit/main.py +55 -0
- visidata/apps/vgit/remote.py +57 -0
- visidata/apps/vgit/repos.py +71 -0
- visidata/apps/vgit/setup.py +37 -0
- visidata/apps/vgit/stash.py +69 -0
- visidata/apps/vgit/status.py +204 -0
- visidata/apps/vgit/statusbar.py +34 -0
- visidata/basesheet.py +59 -50
- visidata/canvas.py +251 -99
- visidata/choose.py +15 -11
- visidata/clean_names.py +29 -0
- visidata/clipboard.py +84 -18
- visidata/cliptext.py +220 -46
- visidata/cmdlog.py +89 -114
- visidata/color.py +142 -56
- visidata/column.py +134 -131
- visidata/ddw/input.ddw +74 -79
- visidata/ddw/regex.ddw +57 -0
- visidata/ddwplay.py +33 -14
- visidata/deprecated.py +77 -3
- visidata/desktop/visidata.desktop +7 -0
- visidata/editor.py +12 -6
- visidata/errors.py +5 -1
- visidata/experimental/__init__.py +0 -0
- visidata/experimental/diff_sheet.py +29 -0
- visidata/experimental/digit_autoedit.py +6 -0
- visidata/experimental/gdrive.py +89 -0
- visidata/experimental/google.py +37 -0
- visidata/experimental/gsheets.py +79 -0
- visidata/experimental/live_search.py +37 -0
- visidata/experimental/liveupdate.py +45 -0
- visidata/experimental/mark.py +133 -0
- visidata/experimental/noahs_tapestry/__init__.py +1 -0
- visidata/experimental/noahs_tapestry/tapestry.py +147 -0
- visidata/experimental/rownum.py +73 -0
- visidata/experimental/slide_cells.py +26 -0
- visidata/expr.py +8 -4
- visidata/extensible.py +32 -6
- visidata/features/__init__.py +0 -0
- visidata/features/addcol_audiometadata.py +42 -0
- visidata/features/addcol_histogram.py +34 -0
- visidata/features/canvas_save_svg.py +69 -0
- visidata/features/change_precision.py +46 -0
- visidata/features/cmdpalette.py +163 -0
- visidata/features/colorbrewer.py +363 -0
- visidata/{colorsheet.py → features/colorsheet.py} +17 -16
- visidata/features/command_server.py +105 -0
- visidata/features/currency_to_usd.py +70 -0
- visidata/{customdate.py → features/customdate.py} +2 -0
- visidata/features/dedupe.py +132 -0
- visidata/{describe.py → features/describe.py} +17 -15
- visidata/features/errors_guide.py +26 -0
- visidata/features/expand_cols.py +202 -0
- visidata/{fill.py → features/fill.py} +4 -2
- visidata/{freeze.py → features/freeze.py} +11 -6
- visidata/features/graph_seaborn.py +79 -0
- visidata/features/helloworld.py +10 -0
- visidata/features/hint_types.py +17 -0
- visidata/{incr.py → features/incr.py} +5 -0
- visidata/{join.py → features/join.py} +107 -53
- visidata/features/known_cols.py +21 -0
- visidata/features/layout.py +62 -0
- visidata/{melt.py → features/melt.py} +33 -21
- visidata/features/normcol.py +118 -0
- visidata/features/open_config.py +7 -0
- visidata/features/open_syspaste.py +18 -0
- visidata/features/ping.py +157 -0
- visidata/features/procmgr.py +208 -0
- visidata/features/random_sample.py +6 -0
- visidata/{regex.py → features/regex.py} +47 -31
- visidata/features/reload_every.py +55 -0
- visidata/features/rename_col_cascade.py +30 -0
- visidata/features/scroll_context.py +60 -0
- visidata/features/select_equal_selected.py +11 -0
- visidata/features/setcol_fake.py +65 -0
- visidata/{slide.py → features/slide.py} +75 -21
- visidata/features/sparkline.py +48 -0
- visidata/features/status_source.py +20 -0
- visidata/{sysedit.py → features/sysedit.py} +2 -1
- visidata/features/sysopen_mailcap.py +46 -0
- visidata/features/term_extras.py +13 -0
- visidata/{transpose.py → features/transpose.py} +5 -4
- visidata/features/type_ipaddr.py +73 -0
- visidata/features/type_url.py +11 -0
- visidata/{unfurl.py → features/unfurl.py} +9 -9
- visidata/{window.py → features/window.py} +2 -2
- visidata/form.py +50 -21
- visidata/freqtbl.py +81 -33
- visidata/fuzzymatch.py +414 -0
- visidata/graph.py +105 -33
- visidata/guide.py +180 -0
- visidata/help.py +75 -44
- visidata/hint.py +39 -0
- visidata/indexsheet.py +109 -0
- visidata/input_history.py +55 -0
- visidata/interface.py +58 -0
- visidata/keys.py +17 -16
- visidata/loaders/__init__.py +9 -0
- visidata/loaders/_pandas.py +61 -21
- visidata/loaders/api_airtable.py +70 -0
- visidata/loaders/api_bitio.py +102 -0
- visidata/loaders/api_matrix.py +148 -0
- visidata/loaders/api_reddit.py +306 -0
- visidata/loaders/api_zulip.py +249 -0
- visidata/loaders/archive.py +41 -7
- visidata/loaders/arrow.py +7 -7
- visidata/loaders/conll.py +49 -0
- visidata/loaders/csv.py +25 -7
- visidata/loaders/eml.py +3 -4
- visidata/loaders/f5log.py +1204 -0
- visidata/loaders/fec.py +325 -0
- visidata/loaders/fixed_width.py +3 -5
- visidata/loaders/frictionless.py +3 -3
- visidata/loaders/geojson.py +8 -5
- visidata/loaders/google.py +48 -0
- visidata/loaders/graphviz.py +4 -4
- visidata/loaders/hdf5.py +4 -4
- visidata/loaders/html.py +48 -10
- visidata/loaders/http.py +84 -30
- visidata/loaders/imap.py +20 -10
- visidata/loaders/jrnl.py +52 -0
- visidata/loaders/json.py +83 -29
- visidata/loaders/jsonla.py +74 -0
- visidata/loaders/lsv.py +15 -11
- visidata/loaders/mailbox.py +40 -0
- visidata/loaders/markdown.py +1 -3
- visidata/loaders/mbtiles.py +4 -5
- visidata/loaders/mysql.py +11 -13
- visidata/loaders/npy.py +7 -7
- visidata/loaders/odf.py +4 -1
- visidata/loaders/orgmode.py +428 -0
- visidata/loaders/pandas_freqtbl.py +14 -20
- visidata/loaders/parquet.py +62 -6
- visidata/loaders/pcap.py +3 -3
- visidata/loaders/pdf.py +4 -3
- visidata/loaders/png.py +19 -13
- visidata/loaders/postgres.py +9 -8
- visidata/loaders/rec.py +7 -3
- visidata/loaders/s3.py +342 -0
- visidata/loaders/sas.py +5 -5
- visidata/loaders/scrape.py +186 -0
- visidata/loaders/shp.py +6 -5
- visidata/loaders/spss.py +5 -6
- visidata/loaders/sqlite.py +68 -28
- visidata/loaders/texttables.py +1 -1
- visidata/loaders/toml.py +60 -0
- visidata/loaders/tsv.py +61 -19
- visidata/loaders/ttf.py +19 -7
- visidata/loaders/unzip_http.py +6 -5
- visidata/loaders/usv.py +1 -1
- visidata/loaders/vcf.py +16 -16
- visidata/loaders/vds.py +10 -7
- visidata/loaders/vdx.py +30 -5
- visidata/loaders/xlsb.py +8 -1
- visidata/loaders/xlsx.py +145 -25
- visidata/loaders/xml.py +6 -3
- visidata/loaders/xword.py +4 -4
- visidata/loaders/yaml.py +15 -5
- visidata/macos.py +1 -1
- visidata/macros.py +130 -41
- visidata/main.py +119 -94
- visidata/mainloop.py +101 -154
- visidata/man/parse_options.py +2 -2
- visidata/man/vd.1 +302 -147
- visidata/man/vd.txt +291 -151
- visidata/memory.py +3 -3
- visidata/menu.py +104 -423
- visidata/metasheets.py +59 -141
- visidata/modify.py +79 -23
- visidata/motd.py +3 -3
- visidata/mouse.py +137 -0
- visidata/movement.py +43 -35
- visidata/optionssheet.py +99 -0
- visidata/path.py +131 -43
- visidata/pivot.py +74 -47
- visidata/plugins.py +65 -192
- visidata/pyobj.py +50 -201
- visidata/rename_col.py +20 -0
- visidata/save.py +42 -20
- visidata/search.py +54 -10
- visidata/selection.py +84 -5
- visidata/settings.py +162 -24
- visidata/sheets.py +229 -257
- visidata/shell.py +51 -21
- visidata/sidebar.py +162 -0
- visidata/sort.py +11 -4
- visidata/statusbar.py +113 -104
- visidata/stored_list.py +43 -0
- visidata/stored_prop.py +38 -0
- visidata/tests/conftest.py +3 -3
- visidata/tests/test_cliptext.py +39 -0
- visidata/tests/test_commands.py +62 -7
- visidata/tests/test_edittext.py +2 -2
- visidata/tests/test_features.py +17 -0
- visidata/tests/test_menu.py +14 -0
- visidata/tests/test_path.py +13 -4
- visidata/text_source.py +53 -0
- visidata/textsheet.py +10 -3
- visidata/theme.py +44 -0
- visidata/themes/__init__.py +0 -0
- visidata/themes/ascii8.py +84 -0
- visidata/themes/asciimono.py +84 -0
- visidata/themes/light.py +17 -0
- visidata/threads.py +87 -39
- visidata/tuiwin.py +22 -0
- visidata/type_currency.py +22 -3
- visidata/type_date.py +31 -9
- visidata/type_floatsi.py +5 -1
- visidata/undo.py +18 -6
- visidata/utils.py +106 -23
- visidata/vdobj.py +28 -17
- visidata/windows.py +10 -0
- visidata/wrappers.py +9 -3
- visidata-3.0.data/data/share/applications/visidata.desktop +7 -0
- {visidata-2.11.dev0.data → visidata-3.0.data}/data/share/man/man1/vd.1 +302 -147
- {visidata-2.11.dev0.data → visidata-3.0.data}/data/share/man/man1/visidata.1 +302 -147
- visidata-3.0.data/scripts/vd2to3.vdx +9 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/METADATA +13 -11
- visidata-3.0.dist-info/RECORD +257 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/WHEEL +1 -1
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/entry_points.txt +0 -1
- visidata/layout.py +0 -44
- visidata/misc.py +0 -5
- visidata-2.11.dev0.dist-info/RECORD +0 -142
- /visidata/{repeat.py → features/repeat.py} +0 -0
- {visidata-2.11.dev0.data → visidata-3.0.data}/scripts/vd +0 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,748 @@
|
|
1
|
+
from copy import copy
|
2
|
+
import functools
|
3
|
+
import operator
|
4
|
+
import re
|
5
|
+
|
6
|
+
from contextlib import contextmanager
|
7
|
+
from visidata import VisiData, Sheet, IndexSheet, vd, date, anytype, vlen, clipdraw, colors, stacktrace, PyobjSheet, BaseSheet, ExpectedException
|
8
|
+
from visidata import ItemColumn, AttrColumn, Column, TextSheet, asyncthread, wrapply, ColumnsSheet, UNLOADED, ExprColumn, undoAttrCopyFunc, ENTER
|
9
|
+
|
10
|
+
vd.option('disp_ibis_sidebar', 'pending_sql', 'which sidebar property to display')
|
11
|
+
vd.option('sql_always_count', False, 'whether to include count of total number of results')
|
12
|
+
vd.option('ibis_limit', 500, 'max number of rows to get in query')
|
13
|
+
|
14
|
+
|
15
|
+
def vdtype_to_ibis_type(t):
|
16
|
+
from ibis.expr import datatypes as dt
|
17
|
+
return {
|
18
|
+
int: dt.int,
|
19
|
+
float: dt.float,
|
20
|
+
date: dt.date,
|
21
|
+
str: dt.string,
|
22
|
+
}.get(t)
|
23
|
+
|
24
|
+
|
25
|
+
def dtype_to_vdtype(dtype):
|
26
|
+
from ibis.expr import datatypes as dt
|
27
|
+
|
28
|
+
try:
|
29
|
+
if isinstance(dtype, dt.Decimal):
|
30
|
+
if dtype.scale == 0:
|
31
|
+
return int
|
32
|
+
else:
|
33
|
+
return float
|
34
|
+
if isinstance(dtype, dt.Integer):
|
35
|
+
return int
|
36
|
+
if isinstance(dtype, dt.Floating):
|
37
|
+
return float
|
38
|
+
if isinstance(dtype, (dt.Date, dt.Timestamp)):
|
39
|
+
return date
|
40
|
+
except TypeError:
|
41
|
+
# For categoricals and other pandas-defined dtypes
|
42
|
+
pass
|
43
|
+
return anytype
|
44
|
+
|
45
|
+
|
46
|
+
@VisiData.api
|
47
|
+
def configure_ibis(vd):
|
48
|
+
import ibis
|
49
|
+
|
50
|
+
vd.aggregator('collect', ibis.expr.types.AnyValue.collect, 'collect a list of values')
|
51
|
+
ibis.options.verbose_log = vd.status
|
52
|
+
if vd.options.debug:
|
53
|
+
ibis.options.verbose = True
|
54
|
+
|
55
|
+
if 'ibis_type' not in set(c.expr for c in ColumnsSheet.columns):
|
56
|
+
ColumnsSheet.columns += [
|
57
|
+
AttrColumn('ibis_type', type=str)
|
58
|
+
]
|
59
|
+
|
60
|
+
|
61
|
+
@VisiData.api
|
62
|
+
def open_vdsql(vd, p, filetype=None):
|
63
|
+
vd.configure_ibis()
|
64
|
+
|
65
|
+
# on-demand aliasing, so we don't need deps for all backends
|
66
|
+
ext_aliases = dict(db='sqlite', ddb='duckdb', sqlite3='sqlite')
|
67
|
+
if p.ext in ext_aliases:
|
68
|
+
setattr(ibis, p.ext, ext_aliases.get(p.ext))
|
69
|
+
|
70
|
+
return IbisTableIndexSheet(p.base_stem, source=p, filetype=None, database_name=None,
|
71
|
+
ibis_conpool=IbisConnectionPool(p), sheet_type=IbisTableSheet)
|
72
|
+
|
73
|
+
vd.open_ibis = vd.open_vdsql
|
74
|
+
|
75
|
+
|
76
|
+
class IbisConnectionPool:
|
77
|
+
def __init__(self, source, pool=None, total=0):
|
78
|
+
self.source = source
|
79
|
+
self.pool = pool if pool is not None else []
|
80
|
+
self.total = total
|
81
|
+
|
82
|
+
def __copy__(self):
|
83
|
+
return IbisConnectionPool(self.source, pool=self.pool, total=self.total)
|
84
|
+
|
85
|
+
@contextmanager
|
86
|
+
def get_conn(self):
|
87
|
+
if not self.pool:
|
88
|
+
import sqlalchemy
|
89
|
+
try:
|
90
|
+
import ibis
|
91
|
+
r = ibis.connect(str(self.source))
|
92
|
+
except sqlalchemy.exc.NoSuchModuleError as e:
|
93
|
+
dialect = str(e).split(':')[-1]
|
94
|
+
vd.warning(f'{dialect} not installed')
|
95
|
+
vd.fail(f'pip install ibis-framework[{dialect}]')
|
96
|
+
else:
|
97
|
+
r = self.pool.pop(0)
|
98
|
+
|
99
|
+
try:
|
100
|
+
yield r
|
101
|
+
finally:
|
102
|
+
self.pool.append(r)
|
103
|
+
|
104
|
+
|
105
|
+
class IbisTableIndexSheet(IndexSheet):
|
106
|
+
# sheet_type = IbisTableSheet # set below
|
107
|
+
|
108
|
+
@property
|
109
|
+
def con(self):
|
110
|
+
return self.ibis_conpool.get_conn()
|
111
|
+
|
112
|
+
def rawSql(self, qstr):
|
113
|
+
with self.con as con:
|
114
|
+
return IbisTableSheet('rawsql',
|
115
|
+
ibis_conpool=self.ibis_conpool,
|
116
|
+
ibis_source=qstr,
|
117
|
+
source=qstr,
|
118
|
+
query=con.sql(qstr))
|
119
|
+
|
120
|
+
def iterload(self):
|
121
|
+
with self.con as con:
|
122
|
+
if self.database_name:
|
123
|
+
con.set_database(self.database_name)
|
124
|
+
|
125
|
+
# use the actual count instead of the returned limit
|
126
|
+
nrows_col = self.column('rows')
|
127
|
+
nrows_col.expr = 'countRows'
|
128
|
+
nrows_col.width += 3
|
129
|
+
|
130
|
+
for tblname in con.list_tables():
|
131
|
+
yield self.sheet_type(tblname,
|
132
|
+
ibis_source=self.source,
|
133
|
+
ibis_filetype=self.filetype,
|
134
|
+
ibis_conpool=self.ibis_conpool,
|
135
|
+
database_name=self.database_name,
|
136
|
+
table_name=tblname,
|
137
|
+
source=self.source,
|
138
|
+
query=None)
|
139
|
+
|
140
|
+
|
141
|
+
class IbisColumn(ItemColumn):
|
142
|
+
@property
|
143
|
+
def ibis_type(self):
|
144
|
+
return self.sheet.query[self.ibis_name].type()
|
145
|
+
|
146
|
+
@asyncthread
|
147
|
+
def memo_aggregate(self, agg, rows):
|
148
|
+
'Show aggregated value in status, and add ibis expr to memory for use later.'
|
149
|
+
|
150
|
+
aggexpr = self.ibis_aggr(agg.name) # ignore rows, do over whole query
|
151
|
+
|
152
|
+
with self.sheet.con as con:
|
153
|
+
aggval = con.execute(aggexpr)
|
154
|
+
|
155
|
+
typedval = wrapply(agg.type or self.type, aggval)
|
156
|
+
dispval = self.format(typedval)
|
157
|
+
k = self.name+'_'+agg.name
|
158
|
+
vd.status(f'{k}={dispval}')
|
159
|
+
vd.memory[k] = aggval
|
160
|
+
# store aggexpr somewhere to use in later subquery
|
161
|
+
|
162
|
+
def expand(self, rows):
|
163
|
+
return self.expand_struct(rows)
|
164
|
+
|
165
|
+
def expand_struct(self, rows):
|
166
|
+
oldexpr = self.sheet.ibis_current_expr
|
167
|
+
struct_field = self.get_ibis_col(oldexpr)
|
168
|
+
# if struct_field is not StructType:
|
169
|
+
# vd.fail('vdsql can only expand Struct columns')
|
170
|
+
|
171
|
+
struct_fields = [struct_field[name] for name in struct_field.names]
|
172
|
+
expandedCols = super().expand(rows) # this must go after ibis_current_expr, because it alters ibis_current_expr
|
173
|
+
fields = []
|
174
|
+
for ibiscol, expcol in zip(struct_fields, expandedCols):
|
175
|
+
fields.append(ibiscol.name(expcol.name))
|
176
|
+
# self.sheet.query = oldexpr.drop([struct_field.get_name()]).mutate(fields)
|
177
|
+
self.sheet.query = oldexpr.mutate(fields)
|
178
|
+
return expandedCols
|
179
|
+
|
180
|
+
|
181
|
+
class LazyIbisColMap:
|
182
|
+
def __init__(self, sheet, q):
|
183
|
+
self._sheet = sheet
|
184
|
+
self._query = q
|
185
|
+
self._map = {col.name: col for col in sheet.columns}
|
186
|
+
|
187
|
+
def __getitem__(self, k):
|
188
|
+
col = self._map[k]
|
189
|
+
return col.get_ibis_col(self._query)
|
190
|
+
|
191
|
+
|
192
|
+
class IbisTableSheet(Sheet):
|
193
|
+
@property
|
194
|
+
def con(self):
|
195
|
+
return self.ibis_conpool.get_conn()
|
196
|
+
|
197
|
+
def choose_sidebar(self):
|
198
|
+
sidebars = ['base_sql', 'pending_sql', 'ibis_current_expr', 'curcol_sql', 'pending_expr']
|
199
|
+
opts = []
|
200
|
+
for s in sidebars:
|
201
|
+
try:
|
202
|
+
opts.append({'key': s, 'value':getattr(self, s)})
|
203
|
+
except Exception as e:
|
204
|
+
if self.options.debug:
|
205
|
+
vd.exceptionCaught()
|
206
|
+
|
207
|
+
vd.options.disp_ibis_sidebar = vd.chooseOne(opts)
|
208
|
+
|
209
|
+
@property
|
210
|
+
def curcol_sql(self):
|
211
|
+
expr = self.cursorCol.get_ibis_col(self.ibis_current_expr)
|
212
|
+
if expr is not None:
|
213
|
+
return self.ibis_to_sql(expr, fragment=True)
|
214
|
+
|
215
|
+
def ibis_to_sql(self, expr, fragment=False):
|
216
|
+
import sqlparse
|
217
|
+
with self.con as con:
|
218
|
+
context = con.compiler.make_context()
|
219
|
+
trclass = con.compiler.translator_class(expr.op(), context=context)
|
220
|
+
if fragment:
|
221
|
+
compiled = trclass.get_result()
|
222
|
+
else:
|
223
|
+
compiled = con.compile(expr)
|
224
|
+
if not isinstance(compiled, str):
|
225
|
+
compiled = str(compiled.compile(compile_kwargs={'literal_binds': True}))
|
226
|
+
return sqlparse.format(compiled, reindent=True, keyword_case='upper', wrap_after=40)
|
227
|
+
|
228
|
+
@property
|
229
|
+
def sidebar(self) -> str:
|
230
|
+
sbtype = self.options.disp_ibis_sidebar
|
231
|
+
if sbtype:
|
232
|
+
txt = str(getattr(self, sbtype, ''))
|
233
|
+
if txt:
|
234
|
+
return f'# {sbtype}\n'+txt
|
235
|
+
|
236
|
+
@property
|
237
|
+
def ibis_locals(self):
|
238
|
+
return LazyIbisColMap(self, self.query)
|
239
|
+
|
240
|
+
def select_row(self, row):
|
241
|
+
k = self.rowkey(row) or vd.fail('need key column to select individual rows')
|
242
|
+
super().selectRow(row)
|
243
|
+
self.ibis_selection.append(self.matchRowKeyExpr(row))
|
244
|
+
|
245
|
+
def stoggle_row(self, row):
|
246
|
+
vd.fail('cannot toggle selection of individual row in vdsql')
|
247
|
+
|
248
|
+
def unselect_row(self, row):
|
249
|
+
super().unselectRow(row)
|
250
|
+
self.ibis_selection = [ self.ibis_filter & ~self.matchRowKeyExpr(row) ]
|
251
|
+
|
252
|
+
def matchRowKeyExpr(self, row):
|
253
|
+
import ibis
|
254
|
+
k = self.rowkey(row) or vd.fail('need key column to select individual rows')
|
255
|
+
|
256
|
+
return functools.reduce(operator.and_, [
|
257
|
+
c.get_ibis_col(self.query, typed=True) == k[i]
|
258
|
+
for i, c in enumerate(self.keyCols)
|
259
|
+
])
|
260
|
+
|
261
|
+
@property
|
262
|
+
def ibis_current_expr(self):
|
263
|
+
return self.get_current_expr(typed=False)
|
264
|
+
|
265
|
+
def get_current_expr(self, typed=False):
|
266
|
+
q = self.query
|
267
|
+
extra_cols = {}
|
268
|
+
for c in self.visibleCols:
|
269
|
+
ibis_col = c.get_ibis_col(q, typed=typed)
|
270
|
+
if ibis_col is not None:
|
271
|
+
extra_cols[c.name] = ibis_col
|
272
|
+
else:
|
273
|
+
vd.warning(f'no column {c.name}')
|
274
|
+
|
275
|
+
if extra_cols:
|
276
|
+
q = q.mutate(**extra_cols)
|
277
|
+
|
278
|
+
return q
|
279
|
+
|
280
|
+
@property
|
281
|
+
def ibis_filter(self):
|
282
|
+
import ibis
|
283
|
+
selectors = [self.ibisCompileExpr(f, self.get_current_expr(typed=True)) for f in self.ibis_selection]
|
284
|
+
if not selectors:
|
285
|
+
return ibis.literal(True)
|
286
|
+
return functools.reduce(operator.or_, selectors)
|
287
|
+
|
288
|
+
@property
|
289
|
+
def pending_expr(self):
|
290
|
+
import ibis
|
291
|
+
q = self.get_current_expr(typed=True)
|
292
|
+
if self.ibis_selection:
|
293
|
+
q = q.filter(self.ibis_filter)
|
294
|
+
|
295
|
+
if self._ordering:
|
296
|
+
colorder = []
|
297
|
+
for col, rev in self._ordering:
|
298
|
+
ibiscol = col.get_ibis_col(q) #1856
|
299
|
+
if rev:
|
300
|
+
ibiscol = ibis.desc(ibiscol)
|
301
|
+
colorder.append(ibiscol)
|
302
|
+
q = q.order_by(colorder)
|
303
|
+
|
304
|
+
return q
|
305
|
+
|
306
|
+
def ibisCompileExpr(self, expr, q):
|
307
|
+
if isinstance(expr, str):
|
308
|
+
return eval(expr, vd.getGlobals(), LazyIbisColMap(self, q))
|
309
|
+
else:
|
310
|
+
return expr
|
311
|
+
|
312
|
+
def evalIbisExpr(self, expr):
|
313
|
+
return eval(expr, vd.getGlobals(), self.ibis_locals)
|
314
|
+
|
315
|
+
@property
|
316
|
+
def base_sql(self):
|
317
|
+
return self.sqlize(self.ibis_current_expr)
|
318
|
+
|
319
|
+
@property
|
320
|
+
def pending_sql(self):
|
321
|
+
return self.sqlize(self.pending_expr)
|
322
|
+
|
323
|
+
def sqlize(self, expr):
|
324
|
+
if vd.options.debug:
|
325
|
+
expr = self.withRowcount(expr)
|
326
|
+
return self.ibis_to_sql(expr)
|
327
|
+
|
328
|
+
@property
|
329
|
+
def substrait(self):
|
330
|
+
from ibis_substrait.compiler.core import SubstraitCompiler
|
331
|
+
compiler = SubstraitCompiler()
|
332
|
+
return compiler.compile(self.ibis_current_expr)
|
333
|
+
|
334
|
+
def withRowcount(self, q):
|
335
|
+
if self.options.sql_always_count:
|
336
|
+
# return q.mutate(__n__=q.count())
|
337
|
+
return q.cross_join(q.aggregate(__n__=lambda t: t.count()))
|
338
|
+
return q
|
339
|
+
|
340
|
+
def beforeLoad(self):
|
341
|
+
self.options.disp_rstatus_fmt = self.options.disp_rstatus_fmt.replace('nRows', 'countRows')
|
342
|
+
self.options.disp_rstatus_fmt = self.options.disp_rstatus_fmt.replace('nSelectedRows', 'countSelectedRows')
|
343
|
+
|
344
|
+
def baseQuery(self, con):
|
345
|
+
'Return base table for {database_name}.{table_name}'
|
346
|
+
import ibis
|
347
|
+
tbl = con.table(self.table_name)
|
348
|
+
return ibis.table(tbl.schema(), name=self.fqtblname(con))
|
349
|
+
|
350
|
+
def fqtblname(self, con) -> str:
|
351
|
+
'Return fully-qualified table name including database/schema, or whatever connection needs to identify this table.'
|
352
|
+
if hasattr(con, '_fully_qualified_name'):
|
353
|
+
return con._fully_qualified_name(self.table_name, self.database_name)
|
354
|
+
return self.table_name
|
355
|
+
|
356
|
+
def iterload(self):
|
357
|
+
with self.con as con:
|
358
|
+
if self.query is None:
|
359
|
+
self.query = self.baseQuery(con)
|
360
|
+
|
361
|
+
self.reloadColumns(self.query) # columns based on query without metadata
|
362
|
+
self.query_result = con.execute(self.withRowcount(self.query),
|
363
|
+
limit=self.options.ibis_limit or None)
|
364
|
+
|
365
|
+
yield from self.query_result.itertuples()
|
366
|
+
|
367
|
+
|
368
|
+
def reloadColumns(self, expr, start=1):
|
369
|
+
oldkeycols = {c.name:c for c in self.keyCols}
|
370
|
+
self._nrows_col = -1
|
371
|
+
|
372
|
+
for i, (colname, dtype) in enumerate(expr.schema().items(), start=start):
|
373
|
+
keycol=oldkeycols.get(colname, Column()).keycol
|
374
|
+
if i-start < self.nKeys:
|
375
|
+
keycol = i+1
|
376
|
+
|
377
|
+
if colname == '__n__':
|
378
|
+
self._nrows_col = i
|
379
|
+
continue
|
380
|
+
|
381
|
+
self.addColumn(IbisColumn(colname, i,
|
382
|
+
type=dtype_to_vdtype(dtype),
|
383
|
+
keycol=keycol,
|
384
|
+
ibis_name=colname))
|
385
|
+
|
386
|
+
@property
|
387
|
+
def countSelectedRows(self):
|
388
|
+
return f'{self.nSelectedRows}+'
|
389
|
+
|
390
|
+
@property
|
391
|
+
def countRows(self):
|
392
|
+
if self.rows is UNLOADED:
|
393
|
+
return None
|
394
|
+
if not self.rows or self._nrows_col < 0:
|
395
|
+
return self.nRows
|
396
|
+
return self.rows[0][self._nrows_col] # __n__
|
397
|
+
|
398
|
+
def groupBy(self, groupByCols):
|
399
|
+
from ibis import _
|
400
|
+
import ibis
|
401
|
+
from ibis.expr import datatypes as dt
|
402
|
+
aggr_cols = [groupByCols[0].ibis_col.count().name('count')]
|
403
|
+
for c in self.visibleCols:
|
404
|
+
aggr_cols.extend(c.ibis_aggrs)
|
405
|
+
|
406
|
+
q = self.ibis_current_expr
|
407
|
+
groupq = q.aggregate(aggr_cols, by=[c.ibis_col for c in groupByCols])
|
408
|
+
try:
|
409
|
+
win = ibis.window(order_by=ibis.NA)
|
410
|
+
except ibis.common.exceptions.IbisTypeError: # ibis bug: there is not yet a good workaround that covers all backends
|
411
|
+
win = ibis.window(order_by=None)
|
412
|
+
groupq = groupq.mutate(percent=_['count']*100 / _['count'].sum().over(win))
|
413
|
+
|
414
|
+
histolen = 40
|
415
|
+
histogram_char = self.options.disp_histogram
|
416
|
+
if histogram_char and len(aggr_cols) == 1:
|
417
|
+
groupq = groupq.mutate(maxcount=_['count'].max())
|
418
|
+
hval = ibis.literal(histogram_char, type=dt.string)
|
419
|
+
|
420
|
+
def _histogram(t):
|
421
|
+
return hval.repeat((histolen*t['count']/t.maxcount).cast(dt.int))
|
422
|
+
|
423
|
+
groupq = groupq.mutate(histogram=_histogram)
|
424
|
+
|
425
|
+
groupq = groupq.order_by(ibis.desc('count'))
|
426
|
+
|
427
|
+
return IbisFreqTable(self.name, *(col.name for col in groupByCols), 'freq',
|
428
|
+
ibis_conpool=self.ibis_conpool,
|
429
|
+
ibis_source=self.ibis_source,
|
430
|
+
source=self,
|
431
|
+
groupByCols=groupByCols,
|
432
|
+
query=groupq,
|
433
|
+
nKeys=len(groupByCols))
|
434
|
+
|
435
|
+
def unfurl_col(self, col):
|
436
|
+
vs = copy(self)
|
437
|
+
vs.names = [self.name, col.name, 'unfurled']
|
438
|
+
vs.query = self.ibis_current_expr.mutate(**{col.name:col.ibis_col.unnest()})
|
439
|
+
vs.cursorVisibleColIndex = self.cursorVisibleColIndex
|
440
|
+
return vs
|
441
|
+
|
442
|
+
def openJoin(self, others, jointype=''):
|
443
|
+
sheets = [self] + others
|
444
|
+
|
445
|
+
sheets[1:] or vd.fail("join requires more than 1 sheet")
|
446
|
+
|
447
|
+
if jointype == 'append':
|
448
|
+
q = self.ibis_current_expr
|
449
|
+
for other in others:
|
450
|
+
q = q.union(other.ibis_current_expr)
|
451
|
+
return IbisTableSheet('&'.join(vs.name for vs in sheets), query=q, ibis_source=self.ibis_source, ibis_conpool=self.ibis_conpool)
|
452
|
+
|
453
|
+
for s in sheets:
|
454
|
+
s.keyCols or vd.fail(f'{s.name} has no key cols to join')
|
455
|
+
|
456
|
+
if jointype in ['extend', 'outer']:
|
457
|
+
jointype = 'left'
|
458
|
+
elif jointype in ['full']:
|
459
|
+
jointype = 'outer'
|
460
|
+
# elif jointype in ['inner']:
|
461
|
+
# jointype = 'inner'
|
462
|
+
|
463
|
+
|
464
|
+
q = self.ibis_current_expr
|
465
|
+
for other in others:
|
466
|
+
preds = [(a.ibis_col == b.ibis_col) for a, b in zip(self.keyCols, other.keyCols)]
|
467
|
+
q = q.join(other.ibis_current_expr, predicates=preds, how=jointype, suffixes=('', '_'+other.name))
|
468
|
+
|
469
|
+
return IbisTableSheet('+'.join(vs.name for vs in sheets), sources=sheets, query=q, ibis_source=self.ibis_source, ibis_conpool=self.ibis_conpool)
|
470
|
+
|
471
|
+
|
472
|
+
@Column.property
|
473
|
+
def ibis_col(col):
|
474
|
+
return col.get_ibis_col(col.sheet.ibis_current_expr)
|
475
|
+
|
476
|
+
|
477
|
+
@Column.api
|
478
|
+
def get_ibis_col(col, query:'ibis.Expr', typed=False) -> 'ibis.Expr':
|
479
|
+
'Return ibis.Expr for `col` within context of `query`, cast by VisiData column type if `typed`.'
|
480
|
+
import ibis.common.exceptions
|
481
|
+
|
482
|
+
r = None
|
483
|
+
if isinstance(col, ExprColumn):
|
484
|
+
r = col.sheet.evalIbisExpr(col.expr)
|
485
|
+
elif isinstance(col, vd.ExpandedColumn):
|
486
|
+
r = query[col.name]
|
487
|
+
elif not hasattr(col, 'ibis_name'):
|
488
|
+
return
|
489
|
+
else:
|
490
|
+
try:
|
491
|
+
r = query[col.ibis_name]
|
492
|
+
except (ibis.common.exceptions.IbisTypeError, AttributeError):
|
493
|
+
r = query[col.name]
|
494
|
+
|
495
|
+
if r is None:
|
496
|
+
return r
|
497
|
+
|
498
|
+
if typed:
|
499
|
+
import ibis.expr.datatypes as dt
|
500
|
+
if col.type is str: r = r.cast(dt.string)
|
501
|
+
if col.type is int: r = r.cast(dt.int)
|
502
|
+
if col.type is float: r = r.cast(dt.float)
|
503
|
+
if col.type is date:
|
504
|
+
if not isinstance(r.type(), (dt.Timestamp, dt.Date)):
|
505
|
+
r = r.cast(dt.date)
|
506
|
+
|
507
|
+
r = r.name(col.name)
|
508
|
+
return r
|
509
|
+
|
510
|
+
|
511
|
+
@Column.property
|
512
|
+
def ibis_aggrs(col):
|
513
|
+
return [col.ibis_aggr(aggname) for aggname in (col.aggstr or '').split()]
|
514
|
+
|
515
|
+
|
516
|
+
@Column.api
|
517
|
+
def ibis_aggr(col, aggname):
|
518
|
+
aggname = {
|
519
|
+
'avg': 'mean',
|
520
|
+
'median': 'approx_median',
|
521
|
+
'mode': 'notimpl',
|
522
|
+
'distinct': 'nunique',
|
523
|
+
'list': 'collect',
|
524
|
+
'stdev': 'std',
|
525
|
+
# 'p99': 'quantile(0.99)',
|
526
|
+
# 'q10': 'quantile([.1,.2,.3,.4,.5,.6,.7,.8,.9])',
|
527
|
+
}.get(aggname, aggname)
|
528
|
+
|
529
|
+
agg = getattr(col.ibis_col, aggname)
|
530
|
+
return agg().name(f'{aggname}_{col.name}')
|
531
|
+
|
532
|
+
|
533
|
+
IbisTableSheet.init('ibis_selection', list, copy=False)
|
534
|
+
IbisTableSheet.init('_sqlscr', lambda: None, copy=False)
|
535
|
+
IbisTableSheet.init('query_result', lambda: None, copy=False)
|
536
|
+
IbisTableSheet.init('ibis_conpool', lambda: None, copy=True)
|
537
|
+
|
538
|
+
@IbisTableSheet.api
|
539
|
+
def stoggle_rows(sheet):
|
540
|
+
sheet.toggle(sheet.rows)
|
541
|
+
sheet.ibis_selection = [~sheet.ibis_filter]
|
542
|
+
|
543
|
+
|
544
|
+
@IbisTableSheet.api
|
545
|
+
def clearSelected(sheet):
|
546
|
+
super(IbisTableSheet, sheet).clearSelected()
|
547
|
+
sheet.ibis_selection.clear()
|
548
|
+
|
549
|
+
|
550
|
+
|
551
|
+
@IbisTableSheet.api
|
552
|
+
def addUndoSelection(sheet):
|
553
|
+
super(IbisTableSheet, sheet).addUndoSelection()
|
554
|
+
vd.addUndo(undoAttrCopyFunc([sheet], 'ibis_selection'))
|
555
|
+
|
556
|
+
|
557
|
+
@IbisTableSheet.api
|
558
|
+
def select_equal_cell(sheet, col, typedval):
|
559
|
+
if sheet.isNullFunc()(typedval):
|
560
|
+
expr = col.ibis_col.isnull()
|
561
|
+
else:
|
562
|
+
q = sheet.get_current_expr(typed=True)
|
563
|
+
ibis_col = col.get_ibis_col(q, typed=True)
|
564
|
+
expr = (ibis_col == typedval)
|
565
|
+
|
566
|
+
sheet.ibis_selection.append(expr)
|
567
|
+
sheet.select(sheet.gatherBy(lambda r,c=col,v=typedval: c.getTypedValue(r) == v), progress=False)
|
568
|
+
|
569
|
+
|
570
|
+
@IbisTableSheet.api
|
571
|
+
def select_col_regex(sheet, col, regex):
|
572
|
+
sheet.selectByIdx(vd.searchRegex(sheet, regex=regex, columns="cursorCol"))
|
573
|
+
sheet.ibis_selection.append(col.get_ibis_col(col.sheet.query).re_search(regex))
|
574
|
+
|
575
|
+
|
576
|
+
@IbisTableSheet.api
|
577
|
+
def select_expr(sheet, expr):
|
578
|
+
sheet.select(sheet.gatherBy(lambda r, sheet=sheet, expr=expr: sheet.evalExpr(expr, r)), progress=False)
|
579
|
+
sheet.ibis_selection.append(expr)
|
580
|
+
|
581
|
+
|
582
|
+
@IbisTableSheet.api
|
583
|
+
def addcol_split(sheet, col, delim):
|
584
|
+
from ibis.expr import datatypes as dt
|
585
|
+
c = Column(col.name+'_split',
|
586
|
+
getter=lambda col,row: col.origCol.getDisplayValue(row).split(col.expr),
|
587
|
+
expr=delim,
|
588
|
+
origCol=col,
|
589
|
+
ibis_name=col.name+'_split')
|
590
|
+
sheet.query = sheet.query.mutate(**{c.name:col.get_ibis_col(sheet.query).cast(dt.string).split(delim)})
|
591
|
+
return c
|
592
|
+
|
593
|
+
|
594
|
+
@IbisTableSheet.api
|
595
|
+
def addcol_subst(sheet, col, before='', after=''):
|
596
|
+
c = Column(col.name + "_re",
|
597
|
+
getter=lambda col,row,before=before,after=after: re.sub(before, after, col.origCol.getDisplayValue(row)),
|
598
|
+
origCol=col,
|
599
|
+
ibis_name=col.name + "_re")
|
600
|
+
sheet.query = sheet.query.mutate(**{c.name:col.get_ibis_col(sheet.query).re_replace(before, after)})
|
601
|
+
return c
|
602
|
+
|
603
|
+
|
604
|
+
@IbisTableSheet.api
|
605
|
+
def addcol_cast(sheet, col):
|
606
|
+
# sheet.query and sheet.ibis_current_expr don't match
|
607
|
+
new_type = vdtype_to_ibis_type(col.type)
|
608
|
+
if new_type is None:
|
609
|
+
vd.warning(f"no type for vd type {col.type}")
|
610
|
+
return
|
611
|
+
expr = sheet.query[col.name].cast(new_type)
|
612
|
+
sheet.query = sheet.query.mutate(**{col.name: expr})
|
613
|
+
newcol = copy(col)
|
614
|
+
col.hide()
|
615
|
+
sheet.addColumnAtCursor(newcol)
|
616
|
+
|
617
|
+
# disable not implemented commands
|
618
|
+
|
619
|
+
@BaseSheet.api
|
620
|
+
def notimpl(sheet):
|
621
|
+
vd.fail(f"{vd.activeCommand.longname} not implemented for {type(sheet).__name__}; copy to new non-ibis sheet with g'")
|
622
|
+
|
623
|
+
|
624
|
+
dml_cmds = '''addcol-bulk addcol-new add-row add-rows
|
625
|
+
copy-cell copy-cells copy-row copy-selected commit-sheet cut-cell cut-cells cut-row cut-selected delete-cell delete-cells delete-row delete-selected
|
626
|
+
edit-cell paste-after paste-before paste-cell setcell-expr
|
627
|
+
setcol-clipboard setcol-expr setcol-fake setcol-fill setcol-format-enum setcol-formatter setcol-incr setcol-incr-step setcol-input setcol-iter setcol-subst setcol-subst-all
|
628
|
+
'''.split()
|
629
|
+
|
630
|
+
neverimpl_cmds = '''
|
631
|
+
select-after select-around-n select-before select-equal-row select-error stoggle-after stoggle-before stoggle-row unselect-after unselect-before select-cols-regex unselect-cols-regex transpose
|
632
|
+
'''.split()
|
633
|
+
|
634
|
+
notimpl_cmds = '''
|
635
|
+
addcol-capture addcol-incr addcol-incr-step addcol-window
|
636
|
+
contract-col expand-col-depth expand-cols expand-cols-depth melt melt-regex pivot random-rows
|
637
|
+
select-error-col select-exact-cell select-exact-row select-rows
|
638
|
+
describe-sheet freq-summary
|
639
|
+
cache-col cache-cols
|
640
|
+
dive-selected-cells
|
641
|
+
dup-rows dup-rows-deep dup-selected-deep
|
642
|
+
'''.split()
|
643
|
+
|
644
|
+
for longname in list(notimpl_cmds) + list(neverimpl_cmds) + list(dml_cmds):
|
645
|
+
if longname:
|
646
|
+
IbisTableSheet.addCommand('', longname, 'notimpl()')
|
647
|
+
|
648
|
+
@IbisTableSheet.api
|
649
|
+
def dup_selected(sheet):
|
650
|
+
vs=copy(sheet)
|
651
|
+
vs.query=sheet.pending_expr
|
652
|
+
vs.incrementName()
|
653
|
+
vd.push(vs)
|
654
|
+
|
655
|
+
|
656
|
+
@BaseSheet.api
|
657
|
+
def incrementName(sheet):
|
658
|
+
if isinstance(sheet.names[-1], int):
|
659
|
+
sheet.names[-1] += 1
|
660
|
+
else:
|
661
|
+
sheet.names = list(sheet.names) + [1]
|
662
|
+
sheet.name = '_'.join(map(str, sheet.names))
|
663
|
+
|
664
|
+
|
665
|
+
@IbisTableSheet.api
|
666
|
+
def dup_limit(sheet, limit:int):
|
667
|
+
vs=copy(sheet)
|
668
|
+
vs.name += f"_top{limit}" if limit else "_all"
|
669
|
+
vs.query=sheet.pending_expr
|
670
|
+
vs.options.ibis_limit=limit
|
671
|
+
return vs
|
672
|
+
|
673
|
+
@IbisTableSheet.api
|
674
|
+
def rawSql(sheet, qstr):
|
675
|
+
with sheet.con as con:
|
676
|
+
return IbisTableSheet('rawsql',
|
677
|
+
ibis_conpool=sheet.ibis_conpool,
|
678
|
+
ibis_source=qstr,
|
679
|
+
source=qstr,
|
680
|
+
query=con.sql(qstr))
|
681
|
+
|
682
|
+
class IbisFreqTable(IbisTableSheet):
|
683
|
+
def freqExpr(self, row):
|
684
|
+
# matching key of grouped columns
|
685
|
+
return functools.reduce(operator.and_, [
|
686
|
+
c.get_ibis_col(self.source.query, typed=True) == self.rowkey(row)[i]
|
687
|
+
for i, c in enumerate(self.groupByCols)
|
688
|
+
])
|
689
|
+
|
690
|
+
def selectRow(self, row):
|
691
|
+
super().selectRow(row)
|
692
|
+
self.source.select(self.gatherBy(lambda r, sheet=self, expr=self.freqExpr(row): sheet.evalExpr(expr, r)), progress=False)
|
693
|
+
self.source.ibis_selection.append(self.freqExpr(row))
|
694
|
+
|
695
|
+
def openRow(self, row):
|
696
|
+
vs = copy(self.source)
|
697
|
+
vs.names = list(vs.names) + ['_'.join(str(x) for x in self.rowkey(row))]
|
698
|
+
vs.query = self.source.query.filter(self.freqExpr(row))
|
699
|
+
return vs
|
700
|
+
|
701
|
+
def openRows(self, rows):
|
702
|
+
'Return sheet with union of all selected items.'
|
703
|
+
vs = copy(self.source)
|
704
|
+
vs.names = list(vs.names) + ['several']
|
705
|
+
|
706
|
+
vs.query = self.source.query.filter([
|
707
|
+
functools.reduce(operator.or_, [self.freqExpr(row) for row in rows])
|
708
|
+
])
|
709
|
+
return vs
|
710
|
+
|
711
|
+
|
712
|
+
IbisTableSheet.addCommand('F', 'freq-col', 'vd.push(groupBy([cursorCol]))')
|
713
|
+
IbisTableSheet.addCommand('gF', 'freq-keys', 'vd.push(groupBy(keyCols))')
|
714
|
+
|
715
|
+
IbisTableSheet.addCommand('"', 'dup-selected', 'vd.push(dup_selected())', 'open duplicate sheet with selected rows (default limit)')
|
716
|
+
IbisTableSheet.addCommand('z"', 'dup-limit', 'vd.push(dup_limit(input("max rows: ", value=options.ibis_limit)))', 'open duplicate sheet with only selected rows (input limit)')
|
717
|
+
IbisTableSheet.addCommand('gz"', 'dup-nolimit', 'vd.push(dup_limit(0))', 'open duplicate sheet with only selected rows (no limit--be careful!)')
|
718
|
+
|
719
|
+
IbisTableSheet.addCommand("'", 'addcol-cast', 'addcol_cast(cursorCol)')
|
720
|
+
|
721
|
+
IbisTableSheet.addCommand('zb', 'sidebar-choose', 'choose_sidebar()', 'choose vdsql sidebar to show')
|
722
|
+
IbisTableSheet.addCommand('', 'exec-sql', 'vd.push(rawSql(input("SQL query: ")))', 'open sheet with results of raw SQL query')
|
723
|
+
IbisTableSheet.addCommand('', 'addcol-subst', 'addColumnAtCursor(addcol_subst(cursorCol, **inputRegexSubstOld("transform column by regex: ")))') # deprecated
|
724
|
+
IbisTableSheet.addCommand('', 'addcol-regex-subst', 'addColumnAtCursor(addcol_subst(cursorCol, **inputRegexSubst("transform column by regex: ")))')
|
725
|
+
IbisTableSheet.addCommand('', 'addcol-split', 'addColumnAtCursor(addcol_split(cursorCol, input("split by delimiter: ", type="delim-split")))')
|
726
|
+
IbisTableSheet.addCommand('gt', 'stoggle-rows', 'stoggle_rows()', 'select rows matching current cell in current column')
|
727
|
+
IbisTableSheet.addCommand(',', 'select-equal-cell', 'select_equal_cell(cursorCol, cursorTypedValue)', 'select rows matching current cell in current column')
|
728
|
+
IbisTableSheet.addCommand('t', 'stoggle-row', 'stoggle_row(cursorRow); cursorDown(1)', 'toggle selection of current row')
|
729
|
+
IbisTableSheet.addCommand('s', 'select-row', 'select_row(cursorRow); cursorDown(1)', 'select current row')
|
730
|
+
IbisTableSheet.addCommand('u', 'unselect-row', 'unselect_row(cursorRow); cursorDown(1)', 'unselect current row')
|
731
|
+
#IbisTableSheet.addCommand('g,', 'select-equal-row', 'select(gatherBy(lambda r,currow=cursorRow,vcols=visibleCols: all([c.getDisplayValue(r) == c.getDisplayValue(currow) for c in vcols])), progress=False)', 'select rows matching current row in all visible columns')
|
732
|
+
#IbisTableSheet.addCommand('z,', 'select-exact-cell', 'select(gatherBy(lambda r,c=cursorCol,v=cursorTypedValue: c.getTypedValue(r) == v), progress=False)', 'select rows matching current cell in current column')
|
733
|
+
#IbisTableSheet.addCommand('gz,', 'select-exact-row', 'select(gatherBy(lambda r,currow=cursorRow,vcols=visibleCols: all([c.getTypedValue(r) == c.getTypedValue(currow) for c in vcols])), progress=False)', 'select rows matching current row in all visible columns')
|
734
|
+
|
735
|
+
IbisTableSheet.addCommand('', 'select-col-regex', 'select_col_regex(cursorCol, inputRegex("select regex: ", type="regex", defaultLast=True))', 'select rows matching regex in current column')
|
736
|
+
|
737
|
+
IbisTableSheet.addCommand('z|', 'select-expr', 'expr=inputExpr("select by expr: "); select_expr(expr)', 'select rows matching Python expression in any visible column')
|
738
|
+
IbisTableSheet.addCommand('z\\', 'unselect-expr', 'expr=inputExpr("unselect by expr: "); unselect(gatherBy(lambda r, sheet=sheet, expr=expr: sheet.evalExpr(expr, r)), progress=False)', 'unselect rows matching Python expression in any visible column')
|
739
|
+
|
740
|
+
IbisFreqTable.addCommand('g'+ENTER, 'open-selected', 'vd.push(openRows(selectedRows))')
|
741
|
+
IbisTableIndexSheet.addCommand('', 'exec-sql', 'vd.push(rawSql(input("SQL query: ")))', 'open sheet with results of raw SQL query')
|
742
|
+
|
743
|
+
IbisTableIndexSheet.class_options.load_lazy = True
|
744
|
+
IbisTableIndexSheet.sheet_type = IbisTableSheet
|
745
|
+
IbisTableSheet.class_options.clean_names = True
|
746
|
+
IbisTableSheet.class_options.regex_flags = ''
|
747
|
+
|
748
|
+
vd.addMenuItem('View', 'Sidebar', 'choose', 'sidebar-choose')
|