visidata 2.11.1__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 +259 -42
- visidata/_open.py +84 -29
- visidata/_types.py +21 -3
- 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
- {vgit → visidata/apps/vgit}/blame.py +5 -2
- {vgit → visidata/apps/vgit}/branch.py +31 -16
- {vgit → visidata/apps/vgit}/config.py +3 -3
- visidata/apps/vgit/diff.py +169 -0
- visidata/apps/vgit/gitsheet.py +161 -0
- {vgit → visidata/apps/vgit}/grep.py +6 -5
- visidata/apps/vgit/log.py +81 -0
- {vgit → visidata/apps/vgit}/main.py +18 -5
- {vgit → visidata/apps/vgit}/remote.py +8 -4
- visidata/apps/vgit/repos.py +71 -0
- {vgit → visidata/apps/vgit}/setup.py +6 -4
- visidata/apps/vgit/stash.py +69 -0
- visidata/apps/vgit/status.py +204 -0
- {vgit → visidata/apps/vgit}/statusbar.py +2 -0
- visidata/basesheet.py +59 -50
- visidata/canvas.py +208 -93
- visidata/choose.py +6 -6
- visidata/clean_names.py +29 -0
- visidata/clipboard.py +73 -17
- visidata/cliptext.py +220 -46
- visidata/cmdlog.py +88 -114
- visidata/color.py +142 -56
- visidata/column.py +121 -129
- 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 +30 -5
- 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} +3 -1
- 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} +32 -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 +2 -4
- 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/macros.py +129 -42
- visidata/main.py +119 -94
- visidata/mainloop.py +101 -155
- visidata/man/parse_options.py +2 -2
- visidata/man/vd.1 +301 -148
- visidata/man/vd.txt +290 -153
- visidata/memory.py +3 -3
- visidata/menu.py +104 -423
- visidata/metasheets.py +59 -141
- visidata/modify.py +78 -23
- visidata/motd.py +3 -3
- visidata/mouse.py +137 -0
- visidata/movement.py +43 -35
- visidata/optionssheet.py +99 -0
- visidata/path.py +113 -32
- visidata/pivot.py +73 -47
- visidata/plugins.py +65 -192
- visidata/pyobj.py +50 -201
- visidata/rename_col.py +20 -0
- visidata/save.py +37 -20
- visidata/search.py +54 -10
- visidata/selection.py +84 -5
- visidata/settings.py +162 -25
- 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 +17 -5
- 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.1.data → visidata-3.0.data}/data/share/man/man1/vd.1 +301 -148
- {visidata-2.11.1.data → visidata-3.0.data}/data/share/man/man1/visidata.1 +301 -148
- visidata-3.0.data/scripts/vd2to3.vdx +9 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/METADATA +12 -8
- visidata-3.0.dist-info/RECORD +257 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/WHEEL +1 -1
- vgit/__init__.py +0 -1
- vgit/gitsheet.py +0 -164
- visidata/layout.py +0 -44
- visidata/misc.py +0 -5
- visidata-2.11.1.data/scripts/vgit +0 -9
- visidata-2.11.1.dist-info/RECORD +0 -155
- {vgit → visidata/apps/vgit}/__main__.py +0 -0
- {vgit → visidata/apps/vgit}/abort.py +0 -0
- /visidata/{repeat.py → features/repeat.py} +0 -0
- {visidata-2.11.1.data → visidata-3.0.data}/scripts/vd +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/entry_points.txt +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/top_level.txt +0 -0
visidata/_types.py
CHANGED
@@ -3,10 +3,19 @@
|
|
3
3
|
import locale
|
4
4
|
from visidata import options, TypedWrapper, vd, VisiData
|
5
5
|
|
6
|
-
|
6
|
+
vd.help_float_fmt = '''
|
7
|
+
- fmt starting with `'%'` (like `%0.2f`) will use [:onclick https://docs.python.org/3.6/library/locale.html#locale.format_string]locale.format_string[/]
|
8
|
+
- other fmt (like `{:.02f}` is passed to Python [:onclick https://docs.python.org/3/library/string.html#custom-string-formatting)]string.format][/]
|
9
|
+
'''
|
10
|
+
|
11
|
+
vd.help_int_fmt = '''
|
12
|
+
- fmt starting with `'%'` (like `%04d`) will use [:onclick https://docs.python.org/3.6/library/locale.html#locale.format_string]locale.format_string[/]
|
13
|
+
- other fmt (like `{:4d}` is passed to Python [:onclick https://docs.python.org/3/library/string.html#custom-string-formatting)]string.format[/]
|
14
|
+
'''
|
15
|
+
|
16
|
+
vd.option('disp_float_fmt', '{:.02f}', 'default fmtstr to format float values', replay=True, help=vd.help_float_fmt)
|
17
|
+
vd.option('disp_int_fmt', '{:d}', 'default fmtstr to format int values', replay=True, help=vd.help_int_fmt)
|
7
18
|
|
8
|
-
vd.option('disp_float_fmt', '{:.02f}', 'default fmtstr to format for float values', replay=True)
|
9
|
-
vd.option('disp_int_fmt', '{:d}', 'default fmtstr to format for int values', replay=True)
|
10
19
|
|
11
20
|
vd.numericTypes = [int,float]
|
12
21
|
|
@@ -97,6 +106,11 @@ vdtype(list, '')
|
|
97
106
|
def isNumeric(vd, col):
|
98
107
|
return col.type in vd.numericTypes
|
99
108
|
|
109
|
+
def deduceType(v):
|
110
|
+
if isinstance(v, (float, int)):
|
111
|
+
return type(v)
|
112
|
+
else:
|
113
|
+
return anytype
|
100
114
|
##
|
101
115
|
|
102
116
|
@vd.numericType('%')
|
@@ -118,3 +132,7 @@ class vlen(int):
|
|
118
132
|
|
119
133
|
def __len__(self):
|
120
134
|
return self
|
135
|
+
|
136
|
+
vd.addGlobals(anytype=anytype,
|
137
|
+
vdtype=vdtype,
|
138
|
+
deduceType=deduceType)
|
visidata/_urlcache.py
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
import os
|
2
2
|
import os.path
|
3
3
|
import time
|
4
|
-
from urllib.request import Request, urlopen
|
5
|
-
import urllib.parse
|
6
4
|
|
7
|
-
from visidata import vd, VisiData, Path,
|
5
|
+
from visidata import vd, VisiData, Path, modtime
|
8
6
|
from visidata.settings import _get_cache_dir
|
9
7
|
|
8
|
+
|
10
9
|
@VisiData.global_api
|
11
10
|
def urlcache(vd, url, days=1, text=True, headers={}):
|
12
11
|
'Return Path object to local cache of url contents.'
|
12
|
+
from urllib.request import Request, urlopen
|
13
|
+
import urllib.parse
|
14
|
+
|
13
15
|
cache_dir = _get_cache_dir()
|
14
16
|
os.makedirs(cache_dir, exist_ok=True)
|
15
17
|
|
@@ -27,7 +29,7 @@ def urlcache(vd, url, days=1, text=True, headers={}):
|
|
27
29
|
ret = fp.read()
|
28
30
|
if text:
|
29
31
|
ret = ret.decode('utf-8').strip()
|
30
|
-
with p.
|
32
|
+
with p.open(mode='w', encoding='utf-8') as fpout:
|
31
33
|
fpout.write(ret)
|
32
34
|
else:
|
33
35
|
with p.open_bytes(mode='w') as fpout:
|
@@ -36,4 +38,15 @@ def urlcache(vd, url, days=1, text=True, headers={}):
|
|
36
38
|
return p
|
37
39
|
|
38
40
|
|
41
|
+
@VisiData.api
|
42
|
+
def enable_requests_cache(vd):
|
43
|
+
try:
|
44
|
+
import requests
|
45
|
+
import requests_cache
|
46
|
+
|
47
|
+
requests_cache.install_cache(str(Path(os.path.join(vd.options.visidata_dir, 'httpcache'))), backend='sqlite', expire_after=24*60*60)
|
48
|
+
except ModuleNotFoundError:
|
49
|
+
vd.warning('install requests_cache for less intrusive scraping')
|
50
|
+
|
51
|
+
|
39
52
|
vd.addGlobals({'urlcache': urlcache})
|
visidata/aggregators.py
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
+
import sys
|
1
2
|
import math
|
2
3
|
import functools
|
3
4
|
import collections
|
4
|
-
|
5
|
+
import statistics
|
5
6
|
|
6
|
-
from visidata import Progress, Column
|
7
|
-
from visidata import
|
7
|
+
from visidata import Progress, Sheet, Column, ColumnsSheet, VisiData
|
8
|
+
from visidata import vd, anytype, vlen, asyncthread, wrapply, AttrDict
|
9
|
+
|
10
|
+
vd.help_aggregators = '# Aggregators Help\nHELPTODO'
|
11
|
+
|
12
|
+
vd.option('null_value', None, 'a value to be counted as null', replay=True)
|
8
13
|
|
9
14
|
|
10
15
|
@Column.api
|
@@ -53,10 +58,11 @@ Column.aggregators = property(aggregators_get, aggregators_set)
|
|
53
58
|
|
54
59
|
|
55
60
|
class Aggregator:
|
56
|
-
def __init__(self, name, type,
|
61
|
+
def __init__(self, name, type, funcRows, funcValues=None, helpstr='foo'):
|
57
62
|
'Define aggregator `name` that calls func(col, rows)'
|
58
63
|
self.type = type
|
59
|
-
self.func =
|
64
|
+
self.func = funcRows # funcRows(col, rows)
|
65
|
+
self.funcValues = funcValues # funcValues(values, *args)
|
60
66
|
self.helpstr = helpstr
|
61
67
|
self.name = name
|
62
68
|
|
@@ -66,19 +72,18 @@ class Aggregator:
|
|
66
72
|
_defaggr = Aggregator
|
67
73
|
|
68
74
|
@VisiData.api
|
69
|
-
def aggregator(vd, name,
|
70
|
-
'Define simple aggregator *name* that calls ``
|
71
|
-
def
|
75
|
+
def aggregator(vd, name, funcValues, helpstr='', *args, type=None):
|
76
|
+
'Define simple aggregator *name* that calls ``funcValues(values, *args)`` to aggregate *values*. Use *type* to force the default type of the aggregated column.'
|
77
|
+
def _funcRows(col, rows): # wrap builtins so they can have a .type
|
72
78
|
vals = list(col.getValues(rows))
|
73
79
|
try:
|
74
|
-
return
|
80
|
+
return funcValues(vals, *args)
|
75
81
|
except Exception as e:
|
76
82
|
if len(vals) == 0:
|
77
83
|
return None
|
78
84
|
return e
|
79
85
|
|
80
|
-
vd.aggregators[name] = _defaggr(name, type,
|
81
|
-
vd.addGlobals({name: func})
|
86
|
+
vd.aggregators[name] = _defaggr(name, type, _funcRows, funcValues=funcValues, helpstr=helpstr) # accepts a srccol + list of rows
|
82
87
|
|
83
88
|
## specific aggregator implementations
|
84
89
|
|
@@ -87,9 +92,11 @@ def mean(vals):
|
|
87
92
|
if vals:
|
88
93
|
return float(sum(vals))/len(vals)
|
89
94
|
|
90
|
-
def
|
91
|
-
|
92
|
-
|
95
|
+
def _vsum(vals):
|
96
|
+
return sum(vals, start=type(vals[0] if len(vals) else 0)()) #1996
|
97
|
+
|
98
|
+
# start parameter in sum() added in Python 3.8
|
99
|
+
vsum = _vsum if sys.version_info[:2] >= (3, 8) else sum
|
93
100
|
|
94
101
|
# http://code.activestate.com/recipes/511478-finding-the-percentile-of-the-values/
|
95
102
|
def _percentile(N, percent, key=lambda x:x):
|
@@ -115,7 +122,7 @@ def _percentile(N, percent, key=lambda x:x):
|
|
115
122
|
|
116
123
|
@functools.lru_cache(100)
|
117
124
|
def percentile(pct, helpstr=''):
|
118
|
-
return _defaggr('p%s'%pct, None, lambda col,rows,pct=pct: _percentile(sorted(col.getValues(rows)), pct/100), helpstr)
|
125
|
+
return _defaggr('p%s'%pct, None, lambda col,rows,pct=pct: _percentile(sorted(col.getValues(rows)), pct/100), helpstr=helpstr)
|
119
126
|
|
120
127
|
def quantiles(q, helpstr):
|
121
128
|
return [percentile(round(100*i/q), helpstr) for i in range(1, q)]
|
@@ -124,13 +131,13 @@ vd.aggregator('min', min, 'minimum value')
|
|
124
131
|
vd.aggregator('max', max, 'maximum value')
|
125
132
|
vd.aggregator('avg', mean, 'arithmetic mean of values', type=float)
|
126
133
|
vd.aggregator('mean', mean, 'arithmetic mean of values', type=float)
|
127
|
-
vd.aggregator('median', median, 'median of values')
|
128
|
-
vd.aggregator('mode', mode, 'mode of values')
|
129
|
-
vd.aggregator('sum',
|
134
|
+
vd.aggregator('median', statistics.median, 'median of values')
|
135
|
+
vd.aggregator('mode', statistics.mode, 'mode of values')
|
136
|
+
vd.aggregator('sum', vsum, 'sum of values')
|
130
137
|
vd.aggregator('distinct', set, 'distinct values', type=vlen)
|
131
138
|
vd.aggregator('count', lambda values: sum(1 for v in values), 'number of values', type=int)
|
132
139
|
vd.aggregator('list', list, 'list of values')
|
133
|
-
vd.aggregator('stdev', stdev, 'standard deviation of values', type=float)
|
140
|
+
vd.aggregator('stdev', statistics.stdev, 'standard deviation of values', type=float)
|
134
141
|
|
135
142
|
vd.aggregators['q3'] = quantiles(3, 'tertiles (33/66th pctile)')
|
136
143
|
vd.aggregators['q4'] = quantiles(4, 'quartiles (25/50/75th pctile)')
|
@@ -143,13 +150,14 @@ for pct in (10, 20, 25, 30, 33, 40, 50, 60, 67, 70, 75, 80, 90, 95, 99):
|
|
143
150
|
vd.aggregators[f'p{pct}'] = percentile(pct, f'{pct}th percentile')
|
144
151
|
|
145
152
|
# returns keys of the row with the max value
|
146
|
-
vd.aggregators['keymax'] = _defaggr('keymax', anytype, lambda col, rows: col.sheet.rowkey(max(col.getValueRows(rows))[1]), 'key of the maximum value')
|
153
|
+
vd.aggregators['keymax'] = _defaggr('keymax', anytype, lambda col, rows: col.sheet.rowkey(max(col.getValueRows(rows))[1]), helpstr='key of the maximum value')
|
147
154
|
|
148
155
|
|
149
156
|
ColumnsSheet.columns += [
|
150
157
|
Column('aggregators',
|
151
158
|
getter=lambda c,r:r.aggstr,
|
152
|
-
setter=lambda c,r,v:setattr(r, 'aggregators', v)
|
159
|
+
setter=lambda c,r,v:setattr(r, 'aggregators', v),
|
160
|
+
help='change the metrics calculated in every Frequency or Pivot derived from the source sheet')
|
153
161
|
]
|
154
162
|
|
155
163
|
@Sheet.api
|
@@ -186,10 +194,42 @@ def memo_aggregate(col, agg, rows):
|
|
186
194
|
@VisiData.property
|
187
195
|
def aggregator_choices(vd):
|
188
196
|
return [
|
189
|
-
|
197
|
+
AttrDict(key=agg, desc=v[0].helpstr if isinstance(v, list) else v.helpstr)
|
198
|
+
for agg, v in vd.aggregators.items()
|
199
|
+
if not agg.startswith('p') # skip all the percentiles, user should use q# instead
|
190
200
|
]
|
191
201
|
|
192
202
|
|
193
|
-
|
194
|
-
|
195
|
-
|
203
|
+
@VisiData.api
|
204
|
+
def chooseAggregators(vd):
|
205
|
+
prompt = 'choose aggregators: '
|
206
|
+
def _fmt_aggr_summary(match, row, trigger_key):
|
207
|
+
formatted_aggrname = match.formatted.get('key', row.key) if match else row.key
|
208
|
+
r = ' '*(len(prompt)-3)
|
209
|
+
r += f'[:keystrokes]{trigger_key}[/] '
|
210
|
+
r += formatted_aggrname
|
211
|
+
if row.desc:
|
212
|
+
r += ' - '
|
213
|
+
r += match.formatted.get('desc', row.desc) if match else row.desc
|
214
|
+
return r
|
215
|
+
|
216
|
+
r = vd.activeSheet.inputPalette(prompt,
|
217
|
+
vd.aggregator_choices,
|
218
|
+
value_key='key',
|
219
|
+
formatter=_fmt_aggr_summary,
|
220
|
+
type='aggregators',
|
221
|
+
help=vd.help_aggregators,
|
222
|
+
multiple=True)
|
223
|
+
|
224
|
+
aggrs = r.split()
|
225
|
+
for aggr in aggrs:
|
226
|
+
vd.usedInputs[aggr] += 1
|
227
|
+
return aggrs
|
228
|
+
|
229
|
+
Sheet.addCommand('+', 'aggregate-col', 'addAggregators([cursorCol], chooseAggregators())', 'Add aggregator to current column')
|
230
|
+
Sheet.addCommand('z+', 'memo-aggregate', 'for agg in chooseAggregators(): cursorCol.memo_aggregate(aggregators[agg], selectedRows or rows)', 'memo result of aggregator over values in selected rows for current column')
|
231
|
+
ColumnsSheet.addCommand('g+', 'aggregate-cols', 'addAggregators(selectedRows or source[0].nonKeyVisibleCols, chooseAggregators())', 'add aggregators to selected source columns')
|
232
|
+
|
233
|
+
vd.addMenuItems('''
|
234
|
+
Column > Add aggregator > aggregate-col
|
235
|
+
''')
|
File without changes
|
@@ -0,0 +1,8 @@
|
|
1
|
+
__all__ = '__title__ __author__ __version__ __description__ __license__ __copyright__'.split()
|
2
|
+
|
3
|
+
__title__ = 'vdsql'
|
4
|
+
__author__ = 'Saul Pwanson <code@saul.pw>'
|
5
|
+
__version__ = '0.3dev'
|
6
|
+
__description__ = 'VisiData for database queries'
|
7
|
+
__license__ = 'Apache License, Version 2.0'
|
8
|
+
__copyright__ = 'Copyright 2022 ' + __author__
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
def main():
|
4
|
+
import ibis
|
5
|
+
import visidata
|
6
|
+
from visidata import main, vd
|
7
|
+
|
8
|
+
from . import __version__
|
9
|
+
visidata.__version_info__ = f'vdsql {__version__}'
|
10
|
+
|
11
|
+
for ext in "db ddb duckdb sqlite sqlite3".split():
|
12
|
+
setattr(vd, f"open_{ext}", vd.open_vdsql)
|
13
|
+
|
14
|
+
for entry_point in ibis.util.backend_entry_points():
|
15
|
+
if entry_point.name in ['bigquery', 'clickhouse', 'snowflake']:
|
16
|
+
# these have their own custom openurl_ funcs already installed
|
17
|
+
continue
|
18
|
+
|
19
|
+
attrname = f"openurl_{entry_point.name}"
|
20
|
+
# when running vdsql directly, override visidata builtin loader with vdsql loader #1929
|
21
|
+
setattr(vd, attrname, vd.open_vdsql)
|
22
|
+
|
23
|
+
main.vd_cli()
|
24
|
+
|
25
|
+
|
26
|
+
if __name__ == "__main__":
|
27
|
+
main()
|