visidata 2.11.1__py3-none-any.whl → 3.0.1__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 +78 -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 +63 -51
- 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 +6 -2
- 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 +22 -4
- 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 +197 -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} +77 -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 +200 -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 +20 -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 +54 -12
- 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 +302 -149
- visidata/man/vd.txt +291 -154
- 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 +55 -205
- 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 +239 -260
- visidata/shell.py +51 -21
- visidata/sidebar.py +162 -0
- visidata/sort.py +11 -4
- visidata/statusbar.py +114 -104
- visidata/stored_list.py +43 -0
- visidata/stored_prop.py +38 -0
- visidata/tests/benchmark.csv +52 -0
- visidata/tests/conftest.py +3 -3
- visidata/tests/test_cliptext.py +39 -0
- visidata/tests/test_commands.py +65 -7
- visidata/tests/test_edittext.py +2 -2
- visidata/tests/test_features.py +28 -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 +89 -40
- 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.1.data/data/share/applications/visidata.desktop +7 -0
- {visidata-2.11.1.data → visidata-3.0.1.data}/data/share/man/man1/vd.1 +302 -149
- {visidata-2.11.1.data → visidata-3.0.1.data}/data/share/man/man1/visidata.1 +302 -149
- visidata-3.0.1.data/scripts/vd2to3.vdx +9 -0
- {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/METADATA +12 -8
- visidata-3.0.1.dist-info/RECORD +258 -0
- {visidata-2.11.1.dist-info → visidata-3.0.1.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.1.data}/scripts/vd +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.1.dist-info}/entry_points.txt +0 -0
- {visidata-2.11.1.dist-info → visidata-3.0.1.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,28 @@
|
|
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 = '''# Choose Aggregators
|
11
|
+
Start typing an aggregator name or description.
|
12
|
+
Multiple aggregators can be added by separating spaces.
|
13
|
+
|
14
|
+
- `Enter` to select top aggregator.
|
15
|
+
- `Tab` to highlight top aggregator.
|
16
|
+
|
17
|
+
## When Aggregator Highlighted
|
18
|
+
|
19
|
+
- `Tab`/`Shift+Tab` to cycle highlighted aggregator.
|
20
|
+
- `Enter` to select aggregators.
|
21
|
+
- `Space` to add highlighted aggregator.
|
22
|
+
- `0-9` to add numbered aggregator.
|
23
|
+
'''
|
24
|
+
|
25
|
+
vd.option('null_value', None, 'a value to be counted as null', replay=True)
|
8
26
|
|
9
27
|
|
10
28
|
@Column.api
|
@@ -53,10 +71,11 @@ Column.aggregators = property(aggregators_get, aggregators_set)
|
|
53
71
|
|
54
72
|
|
55
73
|
class Aggregator:
|
56
|
-
def __init__(self, name, type,
|
74
|
+
def __init__(self, name, type, funcRows, funcValues=None, helpstr='foo'):
|
57
75
|
'Define aggregator `name` that calls func(col, rows)'
|
58
76
|
self.type = type
|
59
|
-
self.func =
|
77
|
+
self.func = funcRows # funcRows(col, rows)
|
78
|
+
self.funcValues = funcValues # funcValues(values, *args)
|
60
79
|
self.helpstr = helpstr
|
61
80
|
self.name = name
|
62
81
|
|
@@ -66,19 +85,18 @@ class Aggregator:
|
|
66
85
|
_defaggr = Aggregator
|
67
86
|
|
68
87
|
@VisiData.api
|
69
|
-
def aggregator(vd, name,
|
70
|
-
'Define simple aggregator *name* that calls ``
|
71
|
-
def
|
88
|
+
def aggregator(vd, name, funcValues, helpstr='', *args, type=None):
|
89
|
+
'Define simple aggregator *name* that calls ``funcValues(values, *args)`` to aggregate *values*. Use *type* to force the default type of the aggregated column.'
|
90
|
+
def _funcRows(col, rows): # wrap builtins so they can have a .type
|
72
91
|
vals = list(col.getValues(rows))
|
73
92
|
try:
|
74
|
-
return
|
93
|
+
return funcValues(vals, *args)
|
75
94
|
except Exception as e:
|
76
95
|
if len(vals) == 0:
|
77
96
|
return None
|
78
97
|
return e
|
79
98
|
|
80
|
-
vd.aggregators[name] = _defaggr(name, type,
|
81
|
-
vd.addGlobals({name: func})
|
99
|
+
vd.aggregators[name] = _defaggr(name, type, _funcRows, funcValues=funcValues, helpstr=helpstr) # accepts a srccol + list of rows
|
82
100
|
|
83
101
|
## specific aggregator implementations
|
84
102
|
|
@@ -87,9 +105,11 @@ def mean(vals):
|
|
87
105
|
if vals:
|
88
106
|
return float(sum(vals))/len(vals)
|
89
107
|
|
90
|
-
def
|
91
|
-
|
92
|
-
|
108
|
+
def _vsum(vals):
|
109
|
+
return sum(vals, start=type(vals[0] if len(vals) else 0)()) #1996
|
110
|
+
|
111
|
+
# start parameter in sum() added in Python 3.8
|
112
|
+
vsum = _vsum if sys.version_info[:2] >= (3, 8) else sum
|
93
113
|
|
94
114
|
# http://code.activestate.com/recipes/511478-finding-the-percentile-of-the-values/
|
95
115
|
def _percentile(N, percent, key=lambda x:x):
|
@@ -115,7 +135,7 @@ def _percentile(N, percent, key=lambda x:x):
|
|
115
135
|
|
116
136
|
@functools.lru_cache(100)
|
117
137
|
def percentile(pct, helpstr=''):
|
118
|
-
return _defaggr('p%s'%pct, None, lambda col,rows,pct=pct: _percentile(sorted(col.getValues(rows)), pct/100), helpstr)
|
138
|
+
return _defaggr('p%s'%pct, None, lambda col,rows,pct=pct: _percentile(sorted(col.getValues(rows)), pct/100), helpstr=helpstr)
|
119
139
|
|
120
140
|
def quantiles(q, helpstr):
|
121
141
|
return [percentile(round(100*i/q), helpstr) for i in range(1, q)]
|
@@ -124,13 +144,13 @@ vd.aggregator('min', min, 'minimum value')
|
|
124
144
|
vd.aggregator('max', max, 'maximum value')
|
125
145
|
vd.aggregator('avg', mean, 'arithmetic mean of values', type=float)
|
126
146
|
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',
|
147
|
+
vd.aggregator('median', statistics.median, 'median of values')
|
148
|
+
vd.aggregator('mode', statistics.mode, 'mode of values')
|
149
|
+
vd.aggregator('sum', vsum, 'sum of values')
|
130
150
|
vd.aggregator('distinct', set, 'distinct values', type=vlen)
|
131
151
|
vd.aggregator('count', lambda values: sum(1 for v in values), 'number of values', type=int)
|
132
152
|
vd.aggregator('list', list, 'list of values')
|
133
|
-
vd.aggregator('stdev', stdev, 'standard deviation of values', type=float)
|
153
|
+
vd.aggregator('stdev', statistics.stdev, 'standard deviation of values', type=float)
|
134
154
|
|
135
155
|
vd.aggregators['q3'] = quantiles(3, 'tertiles (33/66th pctile)')
|
136
156
|
vd.aggregators['q4'] = quantiles(4, 'quartiles (25/50/75th pctile)')
|
@@ -143,13 +163,14 @@ for pct in (10, 20, 25, 30, 33, 40, 50, 60, 67, 70, 75, 80, 90, 95, 99):
|
|
143
163
|
vd.aggregators[f'p{pct}'] = percentile(pct, f'{pct}th percentile')
|
144
164
|
|
145
165
|
# 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')
|
166
|
+
vd.aggregators['keymax'] = _defaggr('keymax', anytype, lambda col, rows: col.sheet.rowkey(max(col.getValueRows(rows))[1]), helpstr='key of the maximum value')
|
147
167
|
|
148
168
|
|
149
169
|
ColumnsSheet.columns += [
|
150
170
|
Column('aggregators',
|
151
171
|
getter=lambda c,r:r.aggstr,
|
152
|
-
setter=lambda c,r,v:setattr(r, 'aggregators', v)
|
172
|
+
setter=lambda c,r,v:setattr(r, 'aggregators', v),
|
173
|
+
help='change the metrics calculated in every Frequency or Pivot derived from the source sheet')
|
153
174
|
]
|
154
175
|
|
155
176
|
@Sheet.api
|
@@ -186,10 +207,42 @@ def memo_aggregate(col, agg, rows):
|
|
186
207
|
@VisiData.property
|
187
208
|
def aggregator_choices(vd):
|
188
209
|
return [
|
189
|
-
|
210
|
+
AttrDict(key=agg, desc=v[0].helpstr if isinstance(v, list) else v.helpstr)
|
211
|
+
for agg, v in vd.aggregators.items()
|
212
|
+
if not agg.startswith('p') # skip all the percentiles, user should use q# instead
|
190
213
|
]
|
191
214
|
|
192
215
|
|
193
|
-
|
194
|
-
|
195
|
-
|
216
|
+
@VisiData.api
|
217
|
+
def chooseAggregators(vd):
|
218
|
+
prompt = 'choose aggregators: '
|
219
|
+
def _fmt_aggr_summary(match, row, trigger_key):
|
220
|
+
formatted_aggrname = match.formatted.get('key', row.key) if match else row.key
|
221
|
+
r = ' '*(len(prompt)-3)
|
222
|
+
r += f'[:keystrokes]{trigger_key}[/] '
|
223
|
+
r += formatted_aggrname
|
224
|
+
if row.desc:
|
225
|
+
r += ' - '
|
226
|
+
r += match.formatted.get('desc', row.desc) if match else row.desc
|
227
|
+
return r
|
228
|
+
|
229
|
+
r = vd.activeSheet.inputPalette(prompt,
|
230
|
+
vd.aggregator_choices,
|
231
|
+
value_key='key',
|
232
|
+
formatter=_fmt_aggr_summary,
|
233
|
+
type='aggregators',
|
234
|
+
help=vd.help_aggregators,
|
235
|
+
multiple=True)
|
236
|
+
|
237
|
+
aggrs = r.split()
|
238
|
+
for aggr in aggrs:
|
239
|
+
vd.usedInputs[aggr] += 1
|
240
|
+
return aggrs
|
241
|
+
|
242
|
+
Sheet.addCommand('+', 'aggregate-col', 'addAggregators([cursorCol], chooseAggregators())', 'Add aggregator to current column')
|
243
|
+
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')
|
244
|
+
ColumnsSheet.addCommand('g+', 'aggregate-cols', 'addAggregators(selectedRows or source[0].nonKeyVisibleCols, chooseAggregators())', 'add aggregators to selected source columns')
|
245
|
+
|
246
|
+
vd.addMenuItems('''
|
247
|
+
Column > Add aggregator > aggregate-col
|
248
|
+
''')
|
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()
|