This repository has been archived by the owner on Apr 7, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrendergrid
executable file
·205 lines (173 loc) · 8.04 KB
/
rendergrid
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/env python
#------------------------------------------------------------------------#
# DataGrid - Tabular Data Rendering Library
# Copyright (C) 2009-2010 Adam Wagner <[email protected]>
# Kenny Parnell <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#------------------------------------------------------------------------#
"""
Simple command-line application for using DataGrid. For usage info:
./rendergrid --help
Non-Python applications (ie php-bindings) use this executable
"""
import sys
import csv
import gzip
from pydoc import pager
from optparse import OptionParser, OptionGroup
from datagrid.core import DataGrid, ColumnDoesNotExistError
from datagrid import datatools, formattools, aggregatetools
def main():
"""Command-line interface main method"""
# Configure & Parse Command-Line args
parser = OptionParser('Usage: %prog [OPTIONS] DATAFILE')
# Ordinary (or generic) options
parser.add_option('--html', action='store_const',
const='datagrid.renderer.html', dest='renderer',
help='Render HTML table')
parser.add_option('--text', action='store_const',
const='datagrid.renderer.ascii', dest='renderer',
help='Render Text (ASCII) table [default]')
parser.add_option('--renderer',
default='datagrid.renderer.ascii',
help='Use custom table renderer')
parser.add_option('--rendereroption', action='append', default=[])
parser.add_option('--stdin', action='store_true')
# Report data options
datagroup = OptionGroup(parser, 'Data defination and manipulation options')
datagroup.add_option('-A', '--autocolumn', action='store_true',
help='Derive column headers from DATAFILE')
datagroup.add_option('-c', '--column', action='append',
help='Set the column header')
datagroup.add_option('-g', '--groupby', action='append',
help='Set the report aggregation', default=[])
datagroup.add_option('-a', '--aggregate', action='append', default=[],
help='Set the aggregation method for a given column. '
'Example: --aggregate="column|sum"')
datagroup.add_option('--calculate', action='append', default=[],
help='Add column by running the given calculation. '
'Example: --calculate="c|{a}+{b}"')
datagroup.add_option('--columndescription', action='append', default=[],
help='Provide a description for the given column. '
'Example: --columndescription="c|Really helpful info."')
datagroup.add_option('-s', '--sort', action='append', default=[],
help='Set the sort column(s)')
datagroup.add_option('-F','--filter', action='append', default=[],
metavar='EXPRESSION', help='Filter out rows that return false for'
' the given expression')
datagroup.add_option('-P','--post_filter', action='append', default=[],
metavar='EXPRESSION', help='Filter out rows that return false for'
' the given expression post aggregration')
datagroup.add_option('--suppressdetail', action='store_true',
help='Suppress detail rows (requires aggregration)')
datagroup.add_option('--type', action='append', default=[],
help='Set the type (str|float) of a column. '
'If no --type declarations are made, each column-type '
'is guessed')
# Display options
displaygroup = OptionGroup(parser, 'Data display options')
displaygroup.add_option('-d', '--display', action='append', default=[],
help='Set display column. '
'(Optional - if not set, all columns are used)')
displaygroup.add_option('-f', '--format', action='append', default=[],
help='Set column formatters')
displaygroup.add_option('-o', '--output', help='Save output to file')
# Parse options
parser.add_option_group(datagroup)
parser.add_option_group(displaygroup)
options, args = parser.parse_args()
# Load table renderer
try:
__import__(options.renderer)
renderer = sys.modules[options.renderer]
# Check for 'Renderer' class, otherwise use loaded module
if hasattr(renderer, 'Renderer'):
renderer = renderer.Renderer()
# Set requested renderer options
for option in options.rendereroption:
key, value = option.split('|')
setattr(renderer, key, value)
except ImportError:
parser.error("%s table renderer could not be found"
% options.renderer)
# Attempt to load datafile
try:
# map data(stdin, compressed, or raw)
if options.stdin:
data = csv.reader(sys.stdin)
elif args[0].endswith('.gz'):
data = csv.reader(gzip.open(args[0], 'rb')) # load data file
else:
data = csv.reader(open(args[0])) # load data file
columns = data.next() if options.autocolumn else options.column
except StopIteration:
parser.error("Data file is empty")
except IndexError:
parser.error("No file was supplied")
except IOError:
parser.error("%s does not exist, or is inaccessable" % args[0])
# Setup column types
if options.type:
types = options.type
else:
data = list(data)
types = options.type or datatools.get_column_types(data)
# Apply type conversions to data
data = datatools.set_column_types(data, types)
# Parse aggregate methods
try:
aggregate = aggregatetools.parse_options(options.aggregate)
except KeyError:
parser.error("Invalid aggregation method or column name")
except ValueError:
parser.error("Invalid format passed to --aggregate method")
# Preset formatters from what type of columns we have
for key, columntype in enumerate(types):
if columntype is float:
options.format.insert(0, '%s|plain_number' % columns[key])
# Parse column formatters
formatters = formattools.parse_options(options.format)
# Parse calculated-column methods
calculations = dict(c.split('|') for c in options.calculate)
# Parse column descriptions
descriptions = dict(d.split('|') for d in options.columndescription)
# Parse sort options
sortby = [s.split('|') if '|' in s else s for s in options.sort]
# Verify aggregate are defined columns
if options.groupby and columns:
invalid_group_options = set(options.groupby) - set(columns)
if invalid_group_options:
parser.error("groupby column(s): '%s' not found in column list" %
"', '".join(invalid_group_options))
# Create datagrid instance and render to stdout
grid = DataGrid(data, columns, descriptions, options.groupby,
aggregate, options.suppressdetail, calculations, sortby,
options.display, formatters, filters=options.filter,
post_aggregate_filters=options.post_filter)
try:
if options.output:
with open(options.output, 'w') as outfile:
outfile.write(grid.render(renderer))
else:
pager(grid.render(renderer))
except ColumnDoesNotExistError, e:
parser.error("Column '%s' could not be found!" % e)
# Run if called directly
if __name__ == '__main__':
try:
main()
except IOError:
# allow graceful use of piping
pass