def ReplaceTableData(self, table_id, row_ids, column_values): old_data = self._engine.fetch_table(table_id, formulas=False) self._engine.out_actions.undo.append( actions.ReplaceTableData(*old_data)) self._engine.out_actions.summary.remove_records(table_id, old_data[1]) self._engine.out_actions.summary.add_records(table_id, row_ids) self._engine.load_table( actions.TableData(table_id, row_ids, column_values))
def create_table_views_action(views_to_table, primary_views): related_views = sorted( set(views_to_table.keys()) - set(primary_views.values())) row_ids = list(xrange(1, len(related_views) + 1)) return actions.ReplaceTableData( '_grist_TableViews', row_ids, { 'tableRef': [views_to_table[v] for v in related_views], 'viewRef': related_views, })
def migration20(tdset): """ Add _grist_Pages table and populate based on existing TableViews entries, ie: tables are sorted alphabetically by their `tableId` and views are gathered within their corresponding table and sorted by their id. """ tables = list( actions.transpose_bulk_action(tdset.all_tables['_grist_Tables'])) table_map = {t.id: t for t in tables} table_views = list( actions.transpose_bulk_action(tdset.all_tables['_grist_TableViews'])) # Old docs may include "Other views", not associated with any table. Don't include those in # table_views_map: they'll get included but not sorted or grouped by tableId. table_views_map = { tv.viewRef: table_map[tv.tableRef].tableId for tv in table_views if tv.tableRef in table_map } views = list( actions.transpose_bulk_action(tdset.all_tables['_grist_Views'])) def view_key(view): """ Returns ("Table1", 2) where "Table1" is the view's tableId and 2 the view id. For primary view (ie: not referenced in _grist_TableViews) returns ("Table1", -1). Useful to get the list of views sorted in the same way as in the Table side pane. We use -1 for primary view to make sure they come first among all the views of the same table. """ if view.id in table_views_map: return (table_views_map[view.id], view.id) # the name of primary view's is the same as the tableId return (view.name, -1) views.sort(key=view_key) row_ids = list(xrange(1, len(views) + 1)) return tdset.apply_doc_actions([ actions.AddTable('_grist_Pages', [ schema.make_column('viewRef', 'Ref:_grist_Views'), schema.make_column('pagePos', 'PositionNumber'), schema.make_column('indentation', 'Int'), ]), actions.ReplaceTableData( '_grist_Pages', row_ids, { 'viewRef': [v.id for v in views], 'pagePos': row_ids, 'indentation': [1 if v.id in table_views_map else 0 for v in views] }) ])
def migration1(tdset): """ Add TabItems table, and populate based on existing sections. """ doc_actions = [] # The very first migration is extra-lax, and creates some tables that are missing in some test # docs. That's only because we did not distinguish schema version before migrations were # implemented. Other migrations should not need such conditionals. if '_grist_Attachments' not in tdset.all_tables: doc_actions.append( actions.AddTable("_grist_Attachments", [ schema.make_column("fileIdent", "Text"), schema.make_column("fileName", "Text"), schema.make_column("fileType", "Text"), schema.make_column("fileSize", "Int"), schema.make_column("timeUploaded", "DateTime") ])) if '_grist_TabItems' not in tdset.all_tables: doc_actions.append( actions.AddTable("_grist_TabItems", [ schema.make_column("tableRef", "Ref:_grist_Tables"), schema.make_column("viewRef", "Ref:_grist_Views"), ])) if 'schemaVersion' not in tdset.all_tables['_grist_DocInfo'].columns: doc_actions.append(add_column('_grist_DocInfo', 'schemaVersion', 'Int')) doc_actions.extend([ add_column('_grist_Attachments', 'imageHeight', 'Int'), add_column('_grist_Attachments', 'imageWidth', 'Int'), ]) view_sections = actions.transpose_bulk_action( tdset.all_tables['_grist_Views_section']) rows = sorted({(s.tableRef, s.parentId) for s in view_sections}) if rows: values = { 'tableRef': [r[0] for r in rows], 'viewRef': [r[1] for r in rows] } row_ids = list(xrange(1, len(rows) + 1)) doc_actions.append( actions.ReplaceTableData('_grist_TabItems', row_ids, values)) return tdset.apply_doc_actions(doc_actions)
def alist(): return [ actions.BulkUpdateRecord("Table1", [1, 2, 3], {'Foo': [10, 20, 30]}), actions.BulkUpdateRecord("Table2", [1, 2, 3], { 'Foo': [10, 20, 30], 'Bar': ['a', 'b', 'c'] }), actions.UpdateRecord("Table1", 17, {'Foo': 10}), actions.UpdateRecord("Table2", 18, { 'Foo': 10, 'Bar': 'a' }), actions.AddRecord("Table1", 17, {'Foo': 10}), actions.BulkAddRecord("Table2", 18, { 'Foo': 10, 'Bar': 'a' }), actions.ReplaceTableData("Table2", 18, { 'Foo': 10, 'Bar': 'a' }), actions.RemoveRecord("Table1", 17), actions.BulkRemoveRecord("Table2", [17, 18]), actions.AddColumn("Table1", "Foo", {"type": "Text"}), actions.RenameColumn("Table1", "Foo", "Bar"), actions.ModifyColumn("Table1", "Foo", {"type": "Text"}), actions.RemoveColumn("Table1", "Foo"), actions.AddTable("THello", [{ "id": "Foo" }, { "id": "Bar" }]), actions.RemoveTable("THello"), actions.RenameTable("THello", "TWorld"), ]
def create_tab_bar_action(views_to_table): row_ids = list(xrange(1, len(views_to_table) + 1)) return actions.ReplaceTableData( '_grist_TabBar', row_ids, {'viewRef': sorted(views_to_table.keys())})
def test_prune_actions(self): # prune_actions is in-place, so we make a new list every time. def alist(): return [ actions.BulkUpdateRecord("Table1", [1, 2, 3], {'Foo': [10, 20, 30]}), actions.BulkUpdateRecord("Table2", [1, 2, 3], { 'Foo': [10, 20, 30], 'Bar': ['a', 'b', 'c'] }), actions.UpdateRecord("Table1", 17, {'Foo': 10}), actions.UpdateRecord("Table2", 18, { 'Foo': 10, 'Bar': 'a' }), actions.AddRecord("Table1", 17, {'Foo': 10}), actions.BulkAddRecord("Table2", 18, { 'Foo': 10, 'Bar': 'a' }), actions.ReplaceTableData("Table2", 18, { 'Foo': 10, 'Bar': 'a' }), actions.RemoveRecord("Table1", 17), actions.BulkRemoveRecord("Table2", [17, 18]), actions.AddColumn("Table1", "Foo", {"type": "Text"}), actions.RenameColumn("Table1", "Foo", "Bar"), actions.ModifyColumn("Table1", "Foo", {"type": "Text"}), actions.RemoveColumn("Table1", "Foo"), actions.AddTable("THello", [{ "id": "Foo" }, { "id": "Bar" }]), actions.RemoveTable("THello"), actions.RenameTable("THello", "TWorld"), ] def prune(table_id, col_id): a = alist() actions.prune_actions(a, table_id, col_id) return a self.assertEqual( prune('Table1', 'Foo'), [ actions.BulkUpdateRecord("Table2", [1, 2, 3], { 'Foo': [10, 20, 30], 'Bar': ['a', 'b', 'c'] }), actions.UpdateRecord("Table2", 18, { 'Foo': 10, 'Bar': 'a' }), actions.BulkAddRecord("Table2", 18, { 'Foo': 10, 'Bar': 'a' }), actions.ReplaceTableData("Table2", 18, { 'Foo': 10, 'Bar': 'a' }), actions.RemoveRecord("Table1", 17), actions.BulkRemoveRecord("Table2", [17, 18]), # It doesn't do anything with column renames; it can be addressed if needed. actions.RenameColumn("Table1", "Foo", "Bar"), # It doesn't do anything with AddTable, which is expected. actions.AddTable("THello", [{ "id": "Foo" }, { "id": "Bar" }]), actions.RemoveTable("THello"), actions.RenameTable("THello", "TWorld"), ]) self.assertEqual(prune('Table2', 'Foo'), [ actions.BulkUpdateRecord("Table1", [1, 2, 3], {'Foo': [10, 20, 30]}), actions.BulkUpdateRecord("Table2", [1, 2, 3], {'Bar': ['a', 'b', 'c']}), actions.UpdateRecord("Table1", 17, {'Foo': 10}), actions.UpdateRecord("Table2", 18, {'Bar': 'a'}), actions.AddRecord("Table1", 17, {'Foo': 10}), actions.BulkAddRecord("Table2", 18, {'Bar': 'a'}), actions.ReplaceTableData("Table2", 18, {'Bar': 'a'}), actions.RemoveRecord("Table1", 17), actions.BulkRemoveRecord("Table2", [17, 18]), actions.AddColumn("Table1", "Foo", {"type": "Text"}), actions.RenameColumn("Table1", "Foo", "Bar"), actions.ModifyColumn("Table1", "Foo", {"type": "Text"}), actions.RemoveColumn("Table1", "Foo"), actions.AddTable("THello", [{ "id": "Foo" }, { "id": "Bar" }]), actions.RemoveTable("THello"), actions.RenameTable("THello", "TWorld"), ])