mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
b82eec714a
Summary: this moves sandbox/grist to core, and adds a requirements.txt file for reconstructing the content of sandbox/thirdparty. Test Plan: existing tests pass. Tested core functionality manually. Tested docker build manually. Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2563
134 lines
5.0 KiB
Python
134 lines
5.0 KiB
Python
from itertools import izip
|
|
import actions
|
|
from usertypes import get_type_default
|
|
|
|
import logger
|
|
log = logger.Logger(__name__, logger.INFO)
|
|
|
|
class TableDataSet(object):
|
|
"""
|
|
TableDataSet represents the full data of a Grist document as a dictionary mapping tableId to
|
|
actions.TableData. It then allows applying arbitrary doc-actions, and updates its representation
|
|
of the document accordingly. The dictionary is available as the object's `all_tables` member.
|
|
|
|
This is used, in particular, for migrations, which need to access data with minimal assumptions
|
|
about its interpretation.
|
|
|
|
Note that to initialize a TableDataSet, the schema is needed, so it should be done by applying
|
|
AddTable actions, followed by BulkAddRecord or ReplaceTableData actions.
|
|
"""
|
|
|
|
def __init__(self):
|
|
# Dictionary of { tableId: actions.TableData object }
|
|
self.all_tables = {}
|
|
|
|
# Dictionary of { tableId: { colId: values }} where values come from AddTable, as modified by
|
|
# Add/ModifyColumn actions.
|
|
self._schema = {}
|
|
|
|
def apply_doc_action(self, action):
|
|
try:
|
|
getattr(self, action.__class__.__name__)(*action)
|
|
except Exception, e:
|
|
log.warn("ERROR applying action %s: %s" % (action, e))
|
|
raise
|
|
|
|
def apply_doc_actions(self, doc_actions):
|
|
for a in doc_actions:
|
|
self.apply_doc_action(a)
|
|
return doc_actions
|
|
|
|
def get_col_info(self, table_id, col_id):
|
|
return self._schema[table_id][col_id]
|
|
|
|
def get_schema(self):
|
|
return self._schema
|
|
|
|
#----------------------------------------
|
|
# Actions on records.
|
|
#----------------------------------------
|
|
def AddRecord(self, table_id, row_id, columns):
|
|
self.BulkAddRecord(table_id, [row_id], {key: [val] for key, val in columns.iteritems()})
|
|
|
|
def BulkAddRecord(self, table_id, row_ids, columns):
|
|
table_data = self.all_tables[table_id]
|
|
table_data.row_ids.extend(row_ids)
|
|
for col, values in table_data.columns.iteritems():
|
|
if col in columns:
|
|
values.extend(columns[col])
|
|
else:
|
|
col_info = self._schema[table_id][col]
|
|
default = get_type_default(col_info['type'])
|
|
values.extend([default] * len(row_ids))
|
|
|
|
def RemoveRecord(self, table_id, row_id):
|
|
return self.BulkRemoveRecord(table_id, [row_id])
|
|
|
|
def BulkRemoveRecord(self, table_id, row_ids):
|
|
table_data = self.all_tables[table_id]
|
|
remove_set = set(row_ids)
|
|
for col, values in table_data.columns.iteritems():
|
|
values[:] = [v for r, v in izip(table_data.row_ids, values) if r not in remove_set]
|
|
table_data.row_ids[:] = [r for r in table_data.row_ids if r not in remove_set]
|
|
|
|
def UpdateRecord(self, table_id, row_id, columns):
|
|
self.BulkUpdateRecord(
|
|
table_id, [row_id], {key: [val] for key, val in columns.iteritems()})
|
|
|
|
def BulkUpdateRecord(self, table_id, row_ids, columns):
|
|
table_data = self.all_tables[table_id]
|
|
rowid_map = {r:i for i, r in enumerate(table_data.row_ids)}
|
|
table_indices = [rowid_map[r] for r in row_ids]
|
|
for col, values in columns.iteritems():
|
|
if col in table_data.columns:
|
|
col_values = table_data.columns[col]
|
|
for i, v in izip(table_indices, values):
|
|
col_values[i] = v
|
|
|
|
def ReplaceTableData(self, table_id, row_ids, columns):
|
|
table_data = self.all_tables[table_id]
|
|
del table_data.row_ids[:]
|
|
for col, values in table_data.columns.iteritems():
|
|
del values[:]
|
|
self.BulkAddRecord(table_id, row_ids, columns)
|
|
|
|
#----------------------------------------
|
|
# Actions on columns.
|
|
#----------------------------------------
|
|
|
|
def AddColumn(self, table_id, col_id, col_info):
|
|
self._schema[table_id][col_id] = col_info
|
|
default = get_type_default(col_info['type'])
|
|
table_data = self.all_tables[table_id]
|
|
table_data.columns[col_id] = [default] * len(table_data.row_ids)
|
|
|
|
def RemoveColumn(self, table_id, col_id):
|
|
self._schema[table_id].pop(col_id, None)
|
|
table_data = self.all_tables[table_id]
|
|
table_data.columns.pop(col_id, None)
|
|
|
|
def RenameColumn(self, table_id, old_col_id, new_col_id):
|
|
self._schema[table_id][new_col_id] = self._schema[table_id].pop(old_col_id)
|
|
table_data = self.all_tables[table_id]
|
|
table_data.columns[new_col_id] = table_data.columns.pop(old_col_id)
|
|
|
|
def ModifyColumn(self, table_id, col_id, col_info):
|
|
self._schema[table_id][col_id].update(col_info)
|
|
|
|
#----------------------------------------
|
|
# Actions on tables.
|
|
#----------------------------------------
|
|
def AddTable(self, table_id, columns):
|
|
self.all_tables[table_id] = actions.TableData(table_id, [], {c['id']: [] for c in columns})
|
|
self._schema[table_id] = {c['id']: c.copy() for c in columns}
|
|
|
|
def RemoveTable(self, table_id):
|
|
del self.all_tables[table_id]
|
|
del self._schema[table_id]
|
|
|
|
def RenameTable(self, old_table_id, new_table_id):
|
|
table_data = self.all_tables.pop(old_table_id)
|
|
self.all_tables[new_table_id] = actions.TableData(new_table_id, table_data.row_ids,
|
|
table_data.columns)
|
|
self._schema[new_table_id] = self._schema.pop(old_table_id)
|