mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Nice summary table IDs
Summary: Changes auto-generated summary table IDs from e.g. `GristSummary_6_Table1` to `Table1_summary_A_B` (meaning `Table1` grouped by `A` and `B`). This makes it easier to write formulas involving summary tables, make API requests, understand logs, etc. Because these don't encode the source table ID as reliably as before, `decode_summary_table_name` now uses the summary table schema info, not just the summary table ID. Specifically, it looks at the type of the `group` column, which is `RefList:<source table id>`. Renaming a source table renames the summary table as before, and now renaming a groupby column renames the summary table as well. Conflicting table names are resolved in the usual way by adding a number at the end, e.g. `Table1_summary_A_B2`. These summary tables are not automatically renamed when the disambiguation is no longer needed. A new migration renames all summary tables to the new scheme, and updates formulas using summary tables with a simple regex. Test Plan: Updated many tests to use the new style of name. Added new Python tests to for resolving conflicts when renaming source tables and groupby columns. Added a test for the migration, including renames in formulas. Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D3508
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
from collections import namedtuple
|
||||
import json
|
||||
import re
|
||||
|
||||
import six
|
||||
|
||||
@@ -79,38 +78,34 @@ def _copy_widget_options(options):
|
||||
return options
|
||||
return json.dumps({k: v for k, v in options.items() if k != "rulesOptions"})
|
||||
|
||||
# To generate code, we need to know for each summary table, what its source table is. It would be
|
||||
# easy if we had access to metadata records, but (at least for now) we generate all code based on
|
||||
# schema only. So we encode the source table name inside of the summary table name.
|
||||
#
|
||||
# The encoding includes the length of the source table name, to avoid the possibility of ambiguity
|
||||
# between the second summary table for "Foo", and the first summary table for "Foo2".
|
||||
#
|
||||
# Note that it means we need to rename summary tables when the source table is renamed.
|
||||
|
||||
def encode_summary_table_name(source_table_name):
|
||||
def encode_summary_table_name(source_table_id, groupby_col_ids):
|
||||
"""
|
||||
Create a summary table name that reliably encodes the source table name. It can be decoded even
|
||||
if a suffix is added to the returned name.
|
||||
Create a summary table name based on the source table ID and the groupby column IDs.
|
||||
"""
|
||||
return "GristSummary_%d_%s" % (len(source_table_name), source_table_name)
|
||||
result = source_table_id + '_summary'
|
||||
if groupby_col_ids:
|
||||
result += '_' + '_'.join(sorted(groupby_col_ids))
|
||||
return result
|
||||
|
||||
|
||||
_summary_re = re.compile(r'GristSummary_(\d+)_')
|
||||
|
||||
def decode_summary_table_name(summary_table_name):
|
||||
def decode_summary_table_name(summary_table_info):
|
||||
"""
|
||||
Extract the name of the source table from the summary table name.
|
||||
Extract the name of the source table from the summary table schema info.
|
||||
"""
|
||||
m = _summary_re.match(summary_table_name)
|
||||
if m:
|
||||
start = m.end(0)
|
||||
length = int(m.group(1))
|
||||
source_name = summary_table_name[start : start + length]
|
||||
if len(source_name) == length:
|
||||
return source_name
|
||||
# To generate code, we need to know for each summary table, what its source table is. It would be
|
||||
# easy if we had access to metadata records, but (at least for now) we generate all code based on
|
||||
# schema only. So we use the type of special 'group' column in the summary table.
|
||||
group_col = summary_table_info.columns.get('group')
|
||||
if (
|
||||
group_col
|
||||
and 'getSummarySourceGroup' in group_col.formula
|
||||
and group_col.type.startswith('RefList:')
|
||||
):
|
||||
return group_col.type[8:]
|
||||
return None
|
||||
|
||||
|
||||
def _group_colinfo(source_table):
|
||||
"""Returns ColInfo() for the 'group' column that must be present in every summary table."""
|
||||
return _make_col_info(colId='group', type='RefList:%s' % source_table.tableId,
|
||||
@@ -202,8 +197,9 @@ class SummaryActions(object):
|
||||
summary_table = next((t for t in source_table.summaryTables if t.summaryKey == key), None)
|
||||
created = False
|
||||
if not summary_table:
|
||||
groupby_col_ids = [c.colId for c in groupby_colinfo]
|
||||
result = self.useractions.doAddTable(
|
||||
encode_summary_table_name(source_table.tableId),
|
||||
encode_summary_table_name(source_table.tableId, groupby_col_ids),
|
||||
[_get_colinfo_dict(ci, with_id=True) for ci in groupby_colinfo + formula_colinfo],
|
||||
summarySourceTableRef=source_table.id,
|
||||
raw_section=True)
|
||||
|
||||
Reference in New Issue
Block a user