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.
Files changed (255) hide show
  1. visidata/__init__.py +72 -91
  2. visidata/_input.py +259 -42
  3. visidata/_open.py +84 -29
  4. visidata/_types.py +21 -3
  5. visidata/_urlcache.py +17 -4
  6. visidata/aggregators.py +65 -25
  7. visidata/apps/__init__.py +0 -0
  8. visidata/apps/vdsql/__about__.py +8 -0
  9. visidata/apps/vdsql/__init__.py +5 -0
  10. visidata/apps/vdsql/__main__.py +27 -0
  11. visidata/apps/vdsql/_ibis.py +748 -0
  12. visidata/apps/vdsql/bigquery.py +61 -0
  13. visidata/apps/vdsql/clickhouse.py +53 -0
  14. visidata/apps/vdsql/setup.py +40 -0
  15. visidata/apps/vdsql/snowflake.py +67 -0
  16. visidata/apps/vgit/__init__.py +13 -0
  17. {vgit → visidata/apps/vgit}/blame.py +5 -2
  18. {vgit → visidata/apps/vgit}/branch.py +31 -16
  19. {vgit → visidata/apps/vgit}/config.py +3 -3
  20. visidata/apps/vgit/diff.py +169 -0
  21. visidata/apps/vgit/gitsheet.py +161 -0
  22. {vgit → visidata/apps/vgit}/grep.py +6 -5
  23. visidata/apps/vgit/log.py +81 -0
  24. {vgit → visidata/apps/vgit}/main.py +18 -5
  25. {vgit → visidata/apps/vgit}/remote.py +8 -4
  26. visidata/apps/vgit/repos.py +71 -0
  27. {vgit → visidata/apps/vgit}/setup.py +6 -4
  28. visidata/apps/vgit/stash.py +69 -0
  29. visidata/apps/vgit/status.py +204 -0
  30. {vgit → visidata/apps/vgit}/statusbar.py +2 -0
  31. visidata/basesheet.py +59 -50
  32. visidata/canvas.py +208 -93
  33. visidata/choose.py +6 -6
  34. visidata/clean_names.py +29 -0
  35. visidata/clipboard.py +73 -17
  36. visidata/cliptext.py +220 -46
  37. visidata/cmdlog.py +88 -114
  38. visidata/color.py +142 -56
  39. visidata/column.py +121 -129
  40. visidata/ddw/input.ddw +74 -79
  41. visidata/ddw/regex.ddw +57 -0
  42. visidata/ddwplay.py +33 -14
  43. visidata/deprecated.py +77 -3
  44. visidata/desktop/visidata.desktop +7 -0
  45. visidata/editor.py +12 -6
  46. visidata/errors.py +5 -1
  47. visidata/experimental/__init__.py +0 -0
  48. visidata/experimental/diff_sheet.py +29 -0
  49. visidata/experimental/digit_autoedit.py +6 -0
  50. visidata/experimental/gdrive.py +89 -0
  51. visidata/experimental/google.py +37 -0
  52. visidata/experimental/gsheets.py +79 -0
  53. visidata/experimental/live_search.py +37 -0
  54. visidata/experimental/liveupdate.py +45 -0
  55. visidata/experimental/mark.py +133 -0
  56. visidata/experimental/noahs_tapestry/__init__.py +1 -0
  57. visidata/experimental/noahs_tapestry/tapestry.py +147 -0
  58. visidata/experimental/rownum.py +73 -0
  59. visidata/experimental/slide_cells.py +26 -0
  60. visidata/expr.py +8 -4
  61. visidata/extensible.py +30 -5
  62. visidata/features/__init__.py +0 -0
  63. visidata/features/addcol_audiometadata.py +42 -0
  64. visidata/features/addcol_histogram.py +34 -0
  65. visidata/features/canvas_save_svg.py +69 -0
  66. visidata/features/change_precision.py +46 -0
  67. visidata/features/cmdpalette.py +163 -0
  68. visidata/features/colorbrewer.py +363 -0
  69. visidata/{colorsheet.py → features/colorsheet.py} +17 -16
  70. visidata/features/command_server.py +105 -0
  71. visidata/features/currency_to_usd.py +70 -0
  72. visidata/{customdate.py → features/customdate.py} +2 -0
  73. visidata/features/dedupe.py +132 -0
  74. visidata/{describe.py → features/describe.py} +17 -15
  75. visidata/features/errors_guide.py +26 -0
  76. visidata/features/expand_cols.py +202 -0
  77. visidata/{fill.py → features/fill.py} +3 -1
  78. visidata/{freeze.py → features/freeze.py} +11 -6
  79. visidata/features/graph_seaborn.py +79 -0
  80. visidata/features/helloworld.py +10 -0
  81. visidata/features/hint_types.py +17 -0
  82. visidata/{incr.py → features/incr.py} +5 -0
  83. visidata/{join.py → features/join.py} +107 -53
  84. visidata/features/known_cols.py +21 -0
  85. visidata/features/layout.py +62 -0
  86. visidata/{melt.py → features/melt.py} +32 -21
  87. visidata/features/normcol.py +118 -0
  88. visidata/features/open_config.py +7 -0
  89. visidata/features/open_syspaste.py +18 -0
  90. visidata/features/ping.py +157 -0
  91. visidata/features/procmgr.py +208 -0
  92. visidata/features/random_sample.py +6 -0
  93. visidata/{regex.py → features/regex.py} +47 -31
  94. visidata/features/reload_every.py +55 -0
  95. visidata/features/rename_col_cascade.py +30 -0
  96. visidata/features/scroll_context.py +60 -0
  97. visidata/features/select_equal_selected.py +11 -0
  98. visidata/features/setcol_fake.py +65 -0
  99. visidata/{slide.py → features/slide.py} +75 -21
  100. visidata/features/sparkline.py +48 -0
  101. visidata/features/status_source.py +20 -0
  102. visidata/{sysedit.py → features/sysedit.py} +2 -1
  103. visidata/features/sysopen_mailcap.py +46 -0
  104. visidata/features/term_extras.py +13 -0
  105. visidata/{transpose.py → features/transpose.py} +5 -4
  106. visidata/features/type_ipaddr.py +73 -0
  107. visidata/features/type_url.py +11 -0
  108. visidata/{unfurl.py → features/unfurl.py} +9 -9
  109. visidata/{window.py → features/window.py} +2 -2
  110. visidata/form.py +50 -21
  111. visidata/freqtbl.py +81 -33
  112. visidata/fuzzymatch.py +414 -0
  113. visidata/graph.py +105 -33
  114. visidata/guide.py +180 -0
  115. visidata/help.py +75 -44
  116. visidata/hint.py +39 -0
  117. visidata/indexsheet.py +109 -0
  118. visidata/input_history.py +55 -0
  119. visidata/interface.py +58 -0
  120. visidata/keys.py +17 -16
  121. visidata/loaders/__init__.py +9 -0
  122. visidata/loaders/_pandas.py +61 -21
  123. visidata/loaders/api_airtable.py +70 -0
  124. visidata/loaders/api_bitio.py +102 -0
  125. visidata/loaders/api_matrix.py +148 -0
  126. visidata/loaders/api_reddit.py +306 -0
  127. visidata/loaders/api_zulip.py +249 -0
  128. visidata/loaders/archive.py +41 -7
  129. visidata/loaders/arrow.py +7 -7
  130. visidata/loaders/conll.py +49 -0
  131. visidata/loaders/csv.py +25 -7
  132. visidata/loaders/eml.py +3 -4
  133. visidata/loaders/f5log.py +1204 -0
  134. visidata/loaders/fec.py +325 -0
  135. visidata/loaders/fixed_width.py +2 -4
  136. visidata/loaders/frictionless.py +3 -3
  137. visidata/loaders/geojson.py +8 -5
  138. visidata/loaders/google.py +48 -0
  139. visidata/loaders/graphviz.py +4 -4
  140. visidata/loaders/hdf5.py +4 -4
  141. visidata/loaders/html.py +48 -10
  142. visidata/loaders/http.py +84 -30
  143. visidata/loaders/imap.py +20 -10
  144. visidata/loaders/jrnl.py +52 -0
  145. visidata/loaders/json.py +83 -29
  146. visidata/loaders/jsonla.py +74 -0
  147. visidata/loaders/lsv.py +15 -11
  148. visidata/loaders/mailbox.py +40 -0
  149. visidata/loaders/markdown.py +1 -3
  150. visidata/loaders/mbtiles.py +4 -5
  151. visidata/loaders/mysql.py +11 -13
  152. visidata/loaders/npy.py +7 -7
  153. visidata/loaders/odf.py +4 -1
  154. visidata/loaders/orgmode.py +428 -0
  155. visidata/loaders/pandas_freqtbl.py +14 -20
  156. visidata/loaders/parquet.py +62 -6
  157. visidata/loaders/pcap.py +3 -3
  158. visidata/loaders/pdf.py +4 -3
  159. visidata/loaders/png.py +19 -13
  160. visidata/loaders/postgres.py +9 -8
  161. visidata/loaders/rec.py +7 -3
  162. visidata/loaders/s3.py +342 -0
  163. visidata/loaders/sas.py +5 -5
  164. visidata/loaders/scrape.py +186 -0
  165. visidata/loaders/shp.py +6 -5
  166. visidata/loaders/spss.py +5 -6
  167. visidata/loaders/sqlite.py +68 -28
  168. visidata/loaders/texttables.py +1 -1
  169. visidata/loaders/toml.py +60 -0
  170. visidata/loaders/tsv.py +61 -19
  171. visidata/loaders/ttf.py +19 -7
  172. visidata/loaders/unzip_http.py +6 -5
  173. visidata/loaders/usv.py +1 -1
  174. visidata/loaders/vcf.py +16 -16
  175. visidata/loaders/vds.py +10 -7
  176. visidata/loaders/vdx.py +30 -5
  177. visidata/loaders/xlsb.py +8 -1
  178. visidata/loaders/xlsx.py +145 -25
  179. visidata/loaders/xml.py +6 -3
  180. visidata/loaders/xword.py +4 -4
  181. visidata/loaders/yaml.py +15 -5
  182. visidata/macros.py +129 -42
  183. visidata/main.py +119 -94
  184. visidata/mainloop.py +101 -155
  185. visidata/man/parse_options.py +2 -2
  186. visidata/man/vd.1 +301 -148
  187. visidata/man/vd.txt +290 -153
  188. visidata/memory.py +3 -3
  189. visidata/menu.py +104 -423
  190. visidata/metasheets.py +59 -141
  191. visidata/modify.py +78 -23
  192. visidata/motd.py +3 -3
  193. visidata/mouse.py +137 -0
  194. visidata/movement.py +43 -35
  195. visidata/optionssheet.py +99 -0
  196. visidata/path.py +113 -32
  197. visidata/pivot.py +73 -47
  198. visidata/plugins.py +65 -192
  199. visidata/pyobj.py +50 -201
  200. visidata/rename_col.py +20 -0
  201. visidata/save.py +37 -20
  202. visidata/search.py +54 -10
  203. visidata/selection.py +84 -5
  204. visidata/settings.py +162 -25
  205. visidata/sheets.py +229 -257
  206. visidata/shell.py +51 -21
  207. visidata/sidebar.py +162 -0
  208. visidata/sort.py +11 -4
  209. visidata/statusbar.py +113 -104
  210. visidata/stored_list.py +43 -0
  211. visidata/stored_prop.py +38 -0
  212. visidata/tests/conftest.py +3 -3
  213. visidata/tests/test_cliptext.py +39 -0
  214. visidata/tests/test_commands.py +62 -7
  215. visidata/tests/test_edittext.py +2 -2
  216. visidata/tests/test_features.py +17 -0
  217. visidata/tests/test_menu.py +14 -0
  218. visidata/tests/test_path.py +13 -4
  219. visidata/text_source.py +53 -0
  220. visidata/textsheet.py +10 -3
  221. visidata/theme.py +44 -0
  222. visidata/themes/__init__.py +0 -0
  223. visidata/themes/ascii8.py +84 -0
  224. visidata/themes/asciimono.py +84 -0
  225. visidata/themes/light.py +17 -0
  226. visidata/threads.py +87 -39
  227. visidata/tuiwin.py +22 -0
  228. visidata/type_currency.py +22 -3
  229. visidata/type_date.py +31 -9
  230. visidata/type_floatsi.py +5 -1
  231. visidata/undo.py +17 -5
  232. visidata/utils.py +106 -23
  233. visidata/vdobj.py +28 -17
  234. visidata/windows.py +10 -0
  235. visidata/wrappers.py +9 -3
  236. visidata-3.0.data/data/share/applications/visidata.desktop +7 -0
  237. {visidata-2.11.1.data → visidata-3.0.data}/data/share/man/man1/vd.1 +301 -148
  238. {visidata-2.11.1.data → visidata-3.0.data}/data/share/man/man1/visidata.1 +301 -148
  239. visidata-3.0.data/scripts/vd2to3.vdx +9 -0
  240. {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/METADATA +12 -8
  241. visidata-3.0.dist-info/RECORD +257 -0
  242. {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/WHEEL +1 -1
  243. vgit/__init__.py +0 -1
  244. vgit/gitsheet.py +0 -164
  245. visidata/layout.py +0 -44
  246. visidata/misc.py +0 -5
  247. visidata-2.11.1.data/scripts/vgit +0 -9
  248. visidata-2.11.1.dist-info/RECORD +0 -155
  249. {vgit → visidata/apps/vgit}/__main__.py +0 -0
  250. {vgit → visidata/apps/vgit}/abort.py +0 -0
  251. /visidata/{repeat.py → features/repeat.py} +0 -0
  252. {visidata-2.11.1.data → visidata-3.0.data}/scripts/vd +0 -0
  253. {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/LICENSE.gpl3 +0 -0
  254. {visidata-2.11.1.dist-info → visidata-3.0.dist-info}/entry_points.txt +0 -0
  255. {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
- #__all__ = ['anytype', 'vdtype', ]
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, options, modtime
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.open_text(mode='w', encoding='utf-8') as fpout:
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
- from statistics import mode, stdev
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, func, helpstr='foo'):
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 = 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, func, helpstr='', *args, type=None):
70
- 'Define simple aggregator *name* that calls ``func(values, *args)`` to aggregate *values*. Use *type* to force the default type of the aggregated column.'
71
- def _func(col, rows): # wrap builtins so they can have a .type
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 func(vals, *args)
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, _func, helpstr)
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 median(values):
91
- L = sorted(values)
92
- return L[len(L)//2]
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', sum, 'sum of values')
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
- {'key': agg, 'desc': v[0].helpstr if isinstance(v, list) else v.helpstr} for agg, v in vd.aggregators.items()
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
- Sheet.addCommand('+', 'aggregate-col', 'addAggregators([cursorCol], chooseMany(aggregator_choices))', 'Add aggregator to current column')
194
- Sheet.addCommand('z+', 'memo-aggregate', 'for agg in chooseMany(aggregator_choices): cursorCol.memo_aggregate(aggregators[agg], selectedRows or rows)', 'memo result of aggregator over values in selected rows for current column')
195
- ColumnsSheet.addCommand('g+', 'aggregate-cols', 'addAggregators(selectedRows or source[0].nonKeyVisibleCols, chooseMany(aggregator_choices))', 'add aggregators to selected source columns')
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,5 @@
1
+ from .__about__ import *
2
+ from ._ibis import *
3
+ from .bigquery import *
4
+ from .snowflake import *
5
+ from .clickhouse import *
@@ -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()