mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Restoring separated transform columns
Summary: Fix for a bug that prevented two users to change column types at the same time. Test Plan: Added and updated Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D3745
This commit is contained in:
parent
5b9242fa7c
commit
e79613b0ed
@ -141,11 +141,11 @@ export class ColumnTransform extends Disposable {
|
|||||||
// started doing something else, and we should finalize the transform.
|
// started doing something else, and we should finalize the transform.
|
||||||
return actions.every(action => (
|
return actions.every(action => (
|
||||||
// ['AddColumn', USER_TABLE, 'gristHelper_Transform', colInfo]
|
// ['AddColumn', USER_TABLE, 'gristHelper_Transform', colInfo]
|
||||||
(action[2] === 'gristHelper_Transform') ||
|
(action[2]?.toString().startsWith('gristHelper_Transform')) ||
|
||||||
// ['AddColumn', USER_TABLE, 'gristHelper_Converted', colInfo]
|
// ['AddColumn', USER_TABLE, 'gristHelper_Converted', colInfo]
|
||||||
(action[2] === 'gristHelper_Converted') ||
|
(action[2]?.toString().startsWith('gristHelper_Converted')) ||
|
||||||
// ['ConvertFromColumn', USER_TABLE, SOURCE_COLUMN, 'gristHelper_Converted']
|
// ['ConvertFromColumn', USER_TABLE, SOURCE_COLUMN, 'gristHelper_Converted']
|
||||||
(action[3] === 'gristHelper_Converted') ||
|
(action[3]?.toString().startsWith('gristHelper_Converted')) ||
|
||||||
// ["SetDisplayFormula", USER_TABLE, ...]
|
// ["SetDisplayFormula", USER_TABLE, ...]
|
||||||
(action[0] === 'SetDisplayFormula') ||
|
(action[0] === 'SetDisplayFormula') ||
|
||||||
// ['UpdateRecord', '_grist_Table_column', transformColId, ...]
|
// ['UpdateRecord', '_grist_Table_column', transformColId, ...]
|
||||||
|
@ -84,7 +84,7 @@ function getRefTableIdFromData(docModel: DocModel, column: ColumnRec): string|nu
|
|||||||
// will be set to the expression to compute the new values from the old ones.
|
// will be set to the expression to compute the new values from the old ones.
|
||||||
// @param toTypeMaybeFull: Type to convert the column to, either full ('Ref:Foo') or pure ('Ref').
|
// @param toTypeMaybeFull: Type to convert the column to, either full ('Ref:Foo') or pure ('Ref').
|
||||||
export async function prepTransformColInfo(docModel: DocModel, origCol: ColumnRec, origDisplayCol: ColumnRec,
|
export async function prepTransformColInfo(docModel: DocModel, origCol: ColumnRec, origDisplayCol: ColumnRec,
|
||||||
toTypeMaybeFull: string): Promise<ColInfo> {
|
toTypeMaybeFull: string, convertedRef: string): Promise<ColInfo> {
|
||||||
const toType = gristTypes.extractTypeFromColType(toTypeMaybeFull);
|
const toType = gristTypes.extractTypeFromColType(toTypeMaybeFull);
|
||||||
const tableData: TableData = docModel.docData.getTable(origCol.table().tableId())!;
|
const tableData: TableData = docModel.docData.getTable(origCol.table().tableId())!;
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ export async function prepTransformColInfo(docModel: DocModel, origCol: ColumnRe
|
|||||||
type: addColTypeSuffix(toTypeMaybeFull, origCol, docModel),
|
type: addColTypeSuffix(toTypeMaybeFull, origCol, docModel),
|
||||||
isFormula: true,
|
isFormula: true,
|
||||||
visibleCol: 0,
|
visibleCol: 0,
|
||||||
formula: "CURRENT_CONVERSION(rec)",
|
formula: `rec.${convertedRef}`,
|
||||||
rules: origCol.rules(),
|
rules: origCol.rules(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import {cssButtonRow} from 'app/client/ui/RightPanelStyles';
|
|||||||
import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
|
import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
|
||||||
import {testId} from 'app/client/ui2018/cssVars';
|
import {testId} from 'app/client/ui2018/cssVars';
|
||||||
import {FieldBuilder} from 'app/client/widgets/FieldBuilder';
|
import {FieldBuilder} from 'app/client/widgets/FieldBuilder';
|
||||||
|
import {ColumnRec} from 'app/client/models/DocModel';
|
||||||
import {NewAbstractWidget} from 'app/client/widgets/NewAbstractWidget';
|
import {NewAbstractWidget} from 'app/client/widgets/NewAbstractWidget';
|
||||||
import {UserAction} from 'app/common/DocActions';
|
import {UserAction} from 'app/common/DocActions';
|
||||||
import {Computed, dom, fromKo, Observable} from 'grainjs';
|
import {Computed, dom, fromKo, Observable} from 'grainjs';
|
||||||
@ -30,6 +31,7 @@ const t = makeT('components.TypeTransformation');
|
|||||||
export class TypeTransform extends ColumnTransform {
|
export class TypeTransform extends ColumnTransform {
|
||||||
private _reviseTypeChange = Observable.create(this, false);
|
private _reviseTypeChange = Observable.create(this, false);
|
||||||
private _transformWidget: Computed<NewAbstractWidget|null>;
|
private _transformWidget: Computed<NewAbstractWidget|null>;
|
||||||
|
private _convertColumn: ColumnRec; // Set in prepare()
|
||||||
|
|
||||||
constructor(gristDoc: GristDoc, fieldBuilder: FieldBuilder) {
|
constructor(gristDoc: GristDoc, fieldBuilder: FieldBuilder) {
|
||||||
super(gristDoc, fieldBuilder);
|
super(gristDoc, fieldBuilder);
|
||||||
@ -96,29 +98,37 @@ export class TypeTransform extends ColumnTransform {
|
|||||||
*/
|
*/
|
||||||
protected async addTransformColumn(toType: string) {
|
protected async addTransformColumn(toType: string) {
|
||||||
const docModel = this.gristDoc.docModel;
|
const docModel = this.gristDoc.docModel;
|
||||||
const colInfo = await TypeConversion.prepTransformColInfo(docModel, this.origColumn, this.origDisplayCol, toType);
|
const newColInfos = await this._tableData.sendTableActions([
|
||||||
|
['AddColumn', 'gristHelper_Converted', {type: 'Any'}],
|
||||||
|
['AddColumn', 'gristHelper_Transform', {type: 'Any'}],
|
||||||
|
]);
|
||||||
|
const gristHelper_ConvertedRef = newColInfos[0].colRef;
|
||||||
|
const gristHelper_TransformRef = newColInfos[1].colRef;
|
||||||
|
this.transformColumn = docModel.columns.getRowModel(gristHelper_TransformRef);
|
||||||
|
this._convertColumn = docModel.columns.getRowModel(gristHelper_ConvertedRef);
|
||||||
|
const colInfo = await TypeConversion.prepTransformColInfo(
|
||||||
|
docModel, this.origColumn,
|
||||||
|
this.origDisplayCol, toType, this._convertColumn.colId.peek());
|
||||||
// NOTE: We could add rules with AddColumn action, but there are some optimizations that converts array values.
|
// NOTE: We could add rules with AddColumn action, but there are some optimizations that converts array values.
|
||||||
const rules = colInfo.rules;
|
const rules = colInfo.rules;
|
||||||
delete (colInfo as any).rules;
|
delete (colInfo as any).rules;
|
||||||
const newColInfos = await this._tableData.sendTableActions([
|
await this._tableData.sendTableActions([
|
||||||
['AddColumn', 'gristHelper_Converted', {...colInfo, isFormula: false, formula: ''}],
|
['ModifyColumn', this._convertColumn.colId.peek(), {...colInfo, isFormula: false, formula: ''}],
|
||||||
['AddColumn', 'gristHelper_Transform', colInfo],
|
['ModifyColumn', this.transformColumn.colId.peek(), colInfo],
|
||||||
]);
|
]);
|
||||||
const transformColRef = newColInfos[1].colRef;
|
|
||||||
if (rules) {
|
if (rules) {
|
||||||
await this.gristDoc.docData.sendActions([
|
await this.gristDoc.docData.sendActions([
|
||||||
['UpdateRecord', '_grist_Tables_column', transformColRef, { rules }]
|
['UpdateRecord', '_grist_Tables_column', gristHelper_TransformRef, { rules }]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
this.transformColumn = docModel.columns.getRowModel(transformColRef);
|
|
||||||
await this.convertValues();
|
await this.convertValues();
|
||||||
return transformColRef;
|
return gristHelper_TransformRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected convertValuesActions(): UserAction[] {
|
protected convertValuesActions(): UserAction[] {
|
||||||
const tableId = this._tableData.tableId;
|
const tableId = this._tableData.tableId;
|
||||||
const srcColId = this.origColumn.colId.peek();
|
const srcColId = this.origColumn.colId.peek();
|
||||||
const dstColId = "gristHelper_Converted";
|
const dstColId = this._convertColumn.colId.peek();
|
||||||
const type = this.transformColumn.type.peek();
|
const type = this.transformColumn.type.peek();
|
||||||
const widgetOptions = this.transformColumn.widgetOptions.peek();
|
const widgetOptions = this.transformColumn.widgetOptions.peek();
|
||||||
const visibleColRef = this.transformColumn.visibleCol.peek();
|
const visibleColRef = this.transformColumn.visibleCol.peek();
|
||||||
@ -153,7 +163,7 @@ export class TypeTransform extends ColumnTransform {
|
|||||||
* Overrides parent method to delete extra column
|
* Overrides parent method to delete extra column
|
||||||
*/
|
*/
|
||||||
protected cleanup() {
|
protected cleanup() {
|
||||||
void this._tableData.sendTableAction(['RemoveColumn', 'gristHelper_Converted']);
|
void this._tableData.sendTableAction(['RemoveColumn', this._convertColumn.colId.peek()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,7 +171,9 @@ export class TypeTransform extends ColumnTransform {
|
|||||||
*/
|
*/
|
||||||
public async setType(toType: string) {
|
public async setType(toType: string) {
|
||||||
const docModel = this.gristDoc.docModel;
|
const docModel = this.gristDoc.docModel;
|
||||||
const colInfo = await TypeConversion.prepTransformColInfo(docModel, this.origColumn, this.origDisplayCol, toType);
|
const colInfo = await TypeConversion.prepTransformColInfo(
|
||||||
|
docModel, this.origColumn, this.origDisplayCol,
|
||||||
|
toType, this._convertColumn.colId.peek());
|
||||||
const tcol = this.transformColumn;
|
const tcol = this.transformColumn;
|
||||||
await tcol.updateColValues(colInfo as any);
|
await tcol.updateColValues(colInfo as any);
|
||||||
}
|
}
|
||||||
|
@ -504,14 +504,6 @@ def N(value):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def CURRENT_CONVERSION(rec):
|
|
||||||
"""
|
|
||||||
Internal function used by Grist during column type conversions. Not available for use in
|
|
||||||
formulas.
|
|
||||||
"""
|
|
||||||
return rec.gristHelper_Converted
|
|
||||||
|
|
||||||
|
|
||||||
def NA():
|
def NA():
|
||||||
"""
|
"""
|
||||||
Returns the "value not available" error, `#N/A`.
|
Returns the "value not available" error, `#N/A`.
|
||||||
|
@ -357,7 +357,7 @@ class TestTriggerFormulas(test_engine.EngineTestCase):
|
|||||||
])
|
])
|
||||||
|
|
||||||
# ModifyColumn doesn't trigger recalcs.
|
# ModifyColumn doesn't trigger recalcs.
|
||||||
out_actions = self.apply_user_action(["ModifyColumn", "Creatures", "Size", {type: 'Numeric'}])
|
out_actions = self.apply_user_action(["ModifyColumn", "Creatures", "Size", {"type": 'Numeric'}])
|
||||||
self.assertEqual(out_actions.calls, {})
|
self.assertEqual(out_actions.calls, {})
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,10 +40,8 @@ _action_types = {}
|
|||||||
# requested.
|
# requested.
|
||||||
DIRECT_ACTION = 0
|
DIRECT_ACTION = 0
|
||||||
|
|
||||||
# Fields of _grist_Tables_column table that may be modified using ModifyColumns useraction.
|
# Fields of _grist_Tables_column table that can't be modified using ModifyColumn useraction.
|
||||||
_modifiable_col_fields = {'type', 'widgetOptions', 'formula', 'isFormula', 'label',
|
_unmodifiable_col_fields = {'colId', 'id', 'parentId'}
|
||||||
'untieColIdFromLabel'}
|
|
||||||
|
|
||||||
# Fields of _grist_Tables_column table that are inherited by group-by columns from their source.
|
# Fields of _grist_Tables_column table that are inherited by group-by columns from their source.
|
||||||
_inherited_groupby_col_fields = {'colId', 'widgetOptions', 'label', 'untieColIdFromLabel'}
|
_inherited_groupby_col_fields = {'colId', 'widgetOptions', 'label', 'untieColIdFromLabel'}
|
||||||
|
|
||||||
@ -1302,11 +1300,6 @@ class UserActions(object):
|
|||||||
))
|
))
|
||||||
)
|
)
|
||||||
|
|
||||||
if transform:
|
|
||||||
# Delete any currently existing transform columns with the same id
|
|
||||||
if self._engine.tables[table_id].has_column(col_id):
|
|
||||||
self.RemoveColumn(table_id, col_id)
|
|
||||||
|
|
||||||
ret = self.doAddColumn(table_id, col_id, col_info)
|
ret = self.doAddColumn(table_id, col_id, col_info)
|
||||||
|
|
||||||
if not transform and table_rec.rawViewSectionRef:
|
if not transform and table_rec.rawViewSectionRef:
|
||||||
@ -1469,7 +1462,7 @@ class UserActions(object):
|
|||||||
# metadata record. We implement the former interface by forwarding to the latter.
|
# metadata record. We implement the former interface by forwarding to the latter.
|
||||||
col = self._docmodel.get_column_rec(table_id, col_id)
|
col = self._docmodel.get_column_rec(table_id, col_id)
|
||||||
|
|
||||||
update_values = {k: v for k, v in six.iteritems(col_info) if k in _modifiable_col_fields}
|
update_values = {k: v for k, v in six.iteritems(col_info) if k not in _unmodifiable_col_fields}
|
||||||
if '_position' in col_info:
|
if '_position' in col_info:
|
||||||
update_values['parentPos'] = col_info['_position']
|
update_values['parentPos'] = col_info['_position']
|
||||||
self._docmodel.update([col], **update_values)
|
self._docmodel.update([col], **update_values)
|
||||||
|
Loading…
Reference in New Issue
Block a user