from collections import OrderedDict
from .cell import Cell
from .column import Column
[docs]class Table(object):
"""
Tables are two-dimensional arrays with fixed schemas.
Columns are ordered dictionaries of id, label and data type.
Rows are ordered dictionaries mirroring columns.
"""
__gviz__version = 0.6
def __init__(self, schema=None, options=None):
"""Sample schema
({'id':'name', 'type':'string', 'label':'Name', 'options':{} },
{'id':'age', 'type':'number',}
)
"""
self.rows = []
self.schema = OrderedDict()
if schema is not None:
for col in schema:
self.add_column(**col)
self.options = options
[docs] def add_column(self, id, type, label=None, options=None):
"""
Add a new column
Columns cannot be added to tables which already contain data.
"""
if id in self.schema:
raise ValueError("Duplicate column ids '{0}'".format(id))
column = Column(id, type, label, options)
self.schema[column.id] = column
if len(self.rows):
raise ValueError("Cannot add columns to tables already containing data")
@property
def options(self):
return self._options
@options.setter
def options(self, options):
"""
Ensure that options are a dictionary
"""
if options is not None and not isinstance(options, dict):
raise ValueError("Options must be a dictionary")
self._options = options
def _append(self, row):
"""
Convert incoming data into table cells
"""
cols = self.schema.values()
cells = OrderedDict()
for col, value in zip(cols, row):
if isinstance(value, tuple):
cell = Cell(*(col.type,) + value)
elif isinstance(value, dict):
value['typ'] = col.type
cell = Cell(**value)
else:
cell = Cell(col.type, value)
cells[col.id] = cell
return cells
[docs] def append(self, row):
"""
Add a row.
Rows are either sequences of values,
or sequences of (value, label, options) tuples,
or sequences of cell dictionaries.
Dictionaries are the most flexible but also the most verbose.
Tuples do not have to be complete but will be exhausted in order, i.e.
you can't have just a value and options.
"""
if len(row) != len(self.schema):
raise ValueError("Row length does not match number of columns")
self.rows.append(self._append(row))
[docs] def extend(self, rows):
"""Add multiple rows of data"""
for row in rows:
self.append(row)
def __iter__(self):
"""Dictionary interface for JSON encoding"""
rows = [{"c":list(r.values())} for r in self.rows]
cols = list(self.schema.values())
js = ['cols', 'rows', 'p']
for k, v in zip(js, [cols, rows, self.options]):
if v is not None:
yield k, v
[docs] def encode(self):
"""
Convenience method for encoding tables
"""
from .encoder import encode
return encode(self)
[docs] def source(self):
"""
Convenience method for encoding a table as a static JSON data source.
This only wraps the table in the API.
"""
from .encoder import encode
d = {}
d['status'] = "OK"
d['reqId'] = 0
d['version'] = self.__gviz__version
d['table'] = self
return 'google.visualization.Query.setResponse(%s)' % encode(d)