Wip/mk/drive drilldown from enso (#10724)

This moves some of the drilldown logic to the enso code and utilises the new 'get_rows' method for Table
This commit is contained in:
marthasharkey 2024-08-12 13:51:51 +01:00 committed by GitHub
parent b277132950
commit 0d5d01a294
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 63 additions and 87 deletions

View File

@ -60,6 +60,7 @@ interface Matrix {
all_rows_count: number
json: unknown[][]
value_type: ValueType[]
get_child_node: string
}
interface Excel_Workbook {
@ -68,6 +69,7 @@ interface Excel_Workbook {
all_rows_count: number
sheet_names: string[]
json: unknown[][]
get_child_node: string
}
interface ObjectMatrix {
@ -76,6 +78,7 @@ interface ObjectMatrix {
all_rows_count: number
json: object[]
value_type: ValueType[]
get_child_node: string
}
interface UnknownTable {
@ -90,6 +93,7 @@ interface UnknownTable {
value_type: ValueType[]
has_index_col: boolean | undefined
links: string[] | undefined
get_child_node: string
}
export enum TextFormatOptions {
@ -187,32 +191,20 @@ const selectableRowLimits = computed(() => {
})
const newNodeSelectorValues = computed(() => {
let selector
let identifierAction
let tooltipValue
let headerName
switch (config.nodeType) {
case COLUMN_NODE_TYPE:
case VECTOR_NODE_TYPE:
selector = INDEX_FIELD_NAME
identifierAction = 'at'
tooltipValue = 'value'
break
case ROW_NODE_TYPE:
selector = 'column'
identifierAction = 'at'
tooltipValue = 'value'
break
case EXCEL_WORKBOOK_NODE_TYPE:
selector = 'Value'
identifierAction = 'read'
tooltipValue = 'sheet'
headerName = 'Sheets'
break
case SQLITE_CONNECTIONS_NODE_TYPE:
case POSTGRES_CONNECTIONS_NODE_TYPE:
selector = 'Value'
identifierAction = 'query'
tooltipValue = 'table'
headerName = 'Tables'
break
@ -221,8 +213,6 @@ const newNodeSelectorValues = computed(() => {
tooltipValue = 'row'
}
return {
selector,
identifierAction,
tooltipValue,
headerName,
}
@ -432,61 +422,37 @@ function toRowField(name: string, valueType?: ValueType | null | undefined) {
}
}
function getAstPattern(selector: string | number, action: string) {
return Pattern.new((ast) =>
Ast.App.positional(
Ast.PropertyAccess.new(ast.module, ast, Ast.identifier(action)!),
typeof selector === 'number' ?
Ast.tryNumberToEnso(selector, ast.module)!
: Ast.TextLiteral.new(selector, ast.module),
),
)
function getAstPattern(selector: string | number, action?: string) {
const identifierAction =
config.nodeType === (COLUMN_NODE_TYPE || VECTOR_NODE_TYPE) ? 'at' : action
if (identifierAction) {
return Pattern.new((ast) =>
Ast.App.positional(
Ast.PropertyAccess.new(ast.module, ast, Ast.identifier(identifierAction)!),
typeof selector === 'number' ?
Ast.tryNumberToEnso(selector, ast.module)!
: Ast.TextLiteral.new(selector, ast.module),
),
)
}
}
const getTablePattern = (index: number) =>
Pattern.new((ast) =>
Ast.OprApp.new(
ast.module,
Ast.App.positional(
Ast.PropertyAccess.new(ast.module, ast, Ast.identifier('rows')!),
Ast.parse('(..All_Rows)'),
),
'.',
Ast.App.positional(
Ast.Ident.new(ast.module, Ast.identifier('get')!),
Ast.tryNumberToEnso(index, ast.module)!,
),
),
)
function createNode(params: CellClickedEvent) {
if (config.nodeType === TABLE_NODE_TYPE || config.nodeType === DB_TABLE_NODE_TYPE) {
function createNode(params: CellClickedEvent, selector: string, action?: string) {
const pattern = getAstPattern(params.data[selector], action)
if (pattern) {
config.createNodes({
content: getTablePattern(params.data[INDEX_FIELD_NAME]),
commit: true,
})
}
if (
newNodeSelectorValues.value.selector !== undefined &&
newNodeSelectorValues.value.selector !== null &&
newNodeSelectorValues.value.identifierAction
) {
config.createNodes({
content: getAstPattern(
params.data[newNodeSelectorValues.value.selector],
newNodeSelectorValues.value.identifierAction,
),
content: pattern,
commit: true,
})
}
}
function toLinkField(fieldName: string): ColDef {
function toLinkField(fieldName: string, getChildAction?: string): ColDef {
return {
headerName:
newNodeSelectorValues.value.headerName ? newNodeSelectorValues.value.headerName : fieldName,
field: fieldName,
onCellDoubleClicked: (params) => createNode(params),
onCellDoubleClicked: (params) => createNode(params, fieldName, getChildAction),
tooltipValueGetter: () => {
return `Double click to view this ${newNodeSelectorValues.value.tooltipValue} in a separate component`
},
@ -515,6 +481,8 @@ watchEffect(() => {
// eslint-disable-next-line camelcase
has_index_col: false,
links: undefined,
// eslint-disable-next-line camelcase
get_child_node: undefined,
}
const options = agGridOptions.value
if (options.api == null) {
@ -533,14 +501,14 @@ watchEffect(() => {
]
rowData = [{ Error: data_.error }]
} else if (data_.type === 'Matrix') {
columnDefs.push(toLinkField(INDEX_FIELD_NAME))
columnDefs.push(toLinkField(INDEX_FIELD_NAME, data_.get_child_node))
for (let i = 0; i < data_.column_count; i++) {
columnDefs.push(toField(i.toString()))
}
rowData = addRowIndex(data_.json)
isTruncated.value = data_.all_rows_count !== data_.json.length
} else if (data_.type === 'Object_Matrix') {
columnDefs.push(toLinkField(INDEX_FIELD_NAME))
columnDefs.push(toLinkField(INDEX_FIELD_NAME, data_.get_child_node))
let keys = new Set<string>()
for (const val of data_.json) {
if (val != null) {
@ -555,14 +523,14 @@ watchEffect(() => {
rowData = addRowIndex(data_.json)
isTruncated.value = data_.all_rows_count !== data_.json.length
} else if (data_.type === 'Excel_Workbook') {
columnDefs = [toLinkField('Value')]
columnDefs = [toLinkField('Value', data_.get_child_node)]
rowData = data_.sheet_names.map((name) => ({ Value: name }))
} else if (Array.isArray(data_.json)) {
columnDefs = [toLinkField(INDEX_FIELD_NAME), toField('Value')]
columnDefs = [toLinkField(INDEX_FIELD_NAME, data_.get_child_node), toField('Value')]
rowData = data_.json.map((row, i) => ({ [INDEX_FIELD_NAME]: i, Value: toRender(row) }))
isTruncated.value = data_.all_rows_count ? data_.all_rows_count !== data_.json.length : false
} else if (data_.json !== undefined) {
columnDefs = data_.links ? [toLinkField('Value')] : [toField('Value')]
columnDefs = data_.links ? [toLinkField('Value', data_.get_child_node)] : [toField('Value')]
rowData =
data_.links ?
data_.links.map((link) => ({
@ -574,12 +542,15 @@ watchEffect(() => {
('header' in data_ ? data_.header : [])?.map((v, i) => {
const valueType = data_.value_type ? data_.value_type[i] : null
if (config.nodeType === ROW_NODE_TYPE) {
return v === 'column' ? toLinkField(v) : toRowField(v, valueType)
return v === 'column' ? toLinkField(v, data_.get_child_node) : toRowField(v, valueType)
}
return toField(v, valueType)
}) ?? []
columnDefs = data_.has_index_col ? [toLinkField(INDEX_FIELD_NAME), ...dataHeader] : dataHeader
columnDefs =
data_.has_index_col ?
[toLinkField(INDEX_FIELD_NAME, data_.get_child_node), ...dataHeader]
: dataHeader
const rows = data_.data && data_.data.length > 0 ? data_.data[0]?.length ?? 0 : 0
rowData = Array.from({ length: rows }, (_, i) => {
const shift = data_.has_index_col ? 1 : 0

View File

@ -306,7 +306,7 @@ type Postgres_Connection
Converts this value to a JSON serializable object.
to_js_object : JS_Object
to_js_object self =
JS_Object.from_pairs [["type", "Postgres_Connection"], ["links", self.connection.tables.at "Name" . to_vector]]
JS_Object.from_pairs [["type", "Postgres_Connection"], ["links", self.connection.tables.at "Name" . to_vector], ["get_child_node", "query"]]
## PRIVATE
get_encoding_name : JDBC_Connection.JDBC_Connection -> Text

View File

@ -287,4 +287,4 @@ type SQLite_Connection
Converts this value to a JSON serializable object.
to_js_object : JS_Object
to_js_object self =
JS_Object.from_pairs [["type", "SQLite_Connection"], ["links", self.connection.tables.at "Name" . to_vector]]
JS_Object.from_pairs [["type", "SQLite_Connection"], ["links", self.connection.tables.at "Name" . to_vector], ["get_child_node", "query"]]

View File

@ -48,7 +48,7 @@ prepare_visualization y max_rows=1000 =
JS_Object.from_pairs [["json", make_json_for_value x]]
_ : Excel_Workbook ->
js_value = x.to_js_object
JS_Object.from_pairs [["json", js_value], ["sheet_names", x . sheet_names], ["type", "Excel_Workbook"]]
JS_Object.from_pairs [["json", js_value], ["sheet_names", x . sheet_names], ["get_child_node", "read"], ["type", "Excel_Workbook"]]
_ : XML_Document -> make_json_for_xml_element x.root_element max_rows "XML_Document"
_ : XML_Element -> make_json_for_xml_element x max_rows
_ -> make_json_for_other x
@ -64,14 +64,15 @@ max_columns = 250
make_json_for_vector : Vector -> Integer -> JS_Object
make_json_for_vector vector max_rows =
all_rows = ["all_rows_count", vector.length]
get_child_node = ["get_child_node", "at"]
truncated = vector.take max_rows
some_values = truncated.any (v->v.is_nothing.not)
if some_values.not then JS_Object.from_pairs [["type", "Vector"], all_rows, ["json", truncated.map make_json_for_value]] else
if some_values.not then JS_Object.from_pairs [["type", "Vector"], all_rows, ["json", truncated.map make_json_for_value], get_child_node] else
matrix = make_json_for_matrix (Builder.new truncated.length) truncated
if matrix.is_nothing.not then JS_Object.from_pairs [["type", "Matrix"], all_rows, ["json", matrix], ["column_count", matrix.fold 0 c->v-> if v.is_nothing then c else c.max v.length]] else
if matrix.is_nothing.not then JS_Object.from_pairs [["type", "Matrix"], all_rows, ["json", matrix], ["column_count", matrix.fold 0 c->v-> if v.is_nothing then c else c.max v.length], get_child_node] else
object_matrix = make_json_for_object_matrix (Builder.new truncated.length) truncated
if object_matrix.is_nothing.not then JS_Object.from_pairs [["type", "Object_Matrix"], all_rows, ["json", object_matrix]] else
JS_Object.from_pairs [["type", "Vector"], all_rows, ["json", truncated.map make_json_for_value]]
if object_matrix.is_nothing.not then JS_Object.from_pairs [["type", "Object_Matrix"], all_rows, ["json", object_matrix], get_child_node] else
JS_Object.from_pairs [["type", "Vector"], all_rows, ["json", truncated.map make_json_for_value], get_child_node]
## PRIVATE
Render Vector of Vector / Array to JSON
@ -124,7 +125,8 @@ make_json_for_dictionary dict max_items key_name="key" =
as_vector = Warning.clear (dict.to_vector.take max_items)
mapped = as_vector . map p-> [p.first.to_text, make_json_for_value p.second]
data = ["data", [mapped.map .first, mapped.map .second]]
JS_Object.from_pairs [header, data, all_rows, ["type", "Map"]]
links = ["get_child_node", "at"]
JS_Object.from_pairs [header, data, all_rows, links, ["type", "Map"]]
## PRIVATE
Render JS_Object to JSON
@ -177,7 +179,8 @@ make_json_for_table dataframe all_rows_count include_index_col =
data = ["data", columns.map get_vector]
all_rows = ["all_rows_count", all_rows_count]
has_index_col = ["has_index_col", include_index_col]
pairs = [header, value_type, data, all_rows, has_index_col, ["type", "Table"]]
links = ["get_child_node", "get_row"]
pairs = [header, value_type, data, all_rows, has_index_col, links, ["type", "Table"]]
JS_Object.from_pairs pairs
## PRIVATE
@ -188,9 +191,11 @@ make_json_for_other x =
value = if js_value.is_a JS_Object . not then js_value else
pairs = [['_display_text_', x.to_display_text]] + js_value.field_names.map f-> [f, make_json_for_value (js_value.get f)]
JS_Object.from_pairs pairs
additional_fields = if js_value.is_a JS_Object . not then [] else
link_fields = if js_value.is_a JS_Object . not then [] else
if js_value.contains_key 'links' then [["links", js_value.get 'links']] else []
JS_Object.from_pairs <| [["json", value]] + additional_fields
additional_fields = if js_value.is_a JS_Object . not then [] else
if js_value.contains_key 'get_child_node' then [["get_child_node", js_value.get 'get_child_node']] else []
JS_Object.from_pairs <| [["json", value]] + additional_fields + link_fields
## PRIVATE
Create JSON serialization of values for the table.

View File

@ -40,17 +40,17 @@ type Foo_Link
Value x
to_js_object : JS_Object
to_js_object self = JS_Object.from_pairs [["x", self.x], ["links", ["a", "b", "c"]]]
to_js_object self = JS_Object.from_pairs [["x", self.x], ["links", ["a", "b", "c"]]]
add_specs suite_builder =
make_json header data all_rows value_type has_index_col =
make_json header data all_rows value_type has_index_col get_child_node =
p_header = ["header", header]
p_data = ["data", data]
p_all_rows = ["all_rows_count", all_rows]
p_value_type = ["value_type", value_type]
p_has_index_col = ["has_index_col", has_index_col]
pairs = [p_header, p_value_type, p_data, p_all_rows, p_has_index_col, ["type", "Table"]]
p_get_child_node = ["get_child_node", get_child_node]
pairs = [p_header, p_value_type, p_data, p_all_rows, p_has_index_col, p_get_child_node, ["type", "Table"]]
JS_Object.from_pairs pairs . to_text
suite_builder.group "Table Visualization" group_builder->
@ -60,45 +60,45 @@ add_specs suite_builder =
vis = Visualization.prepare_visualization data.t 1
value_type_int = JS_Object.from_pairs [["type", "Value_Type"], ["constructor", "Integer"], ["display_text", "Integer (64 bits)"], ["bits", 64]]
value_type_char = JS_Object.from_pairs [["type", "Value_Type"], ["constructor", "Char"], ["display_text", "Char (variable length, max_size=unlimited)"], ["size", Nothing], ["variable_length", True]]
json = make_json header=["A", "B", "C"] data=[['a'], [2], [3]] all_rows=3 value_type=[value_type_char, value_type_int, value_type_int] has_index_col=True
json = make_json header=["A", "B", "C"] data=[['a'], [2], [3]] all_rows=3 value_type=[value_type_char, value_type_int, value_type_int] has_index_col=True get_child_node="get_row"
vis . should_equal json
group_builder.specify "should visualize database columns" <|
vis = Visualization.prepare_visualization (data.t.at "A") 2
value_type_char = JS_Object.from_pairs [["type", "Value_Type"], ["constructor", "Char"], ["display_text", "Char (variable length, max_size=unlimited)"], ["size", Nothing], ["variable_length", True]]
value_type_float = JS_Object.from_pairs [["type", "Value_Type"], ["constructor", "Float"], ["display_text", "Float (64 bits)"], ["bits", 64]]
json = make_json header=["A"] data=[['a', 'a']] all_rows=3 value_type=[value_type_char] has_index_col=True
json = make_json header=["A"] data=[['a', 'a']] all_rows=3 value_type=[value_type_char] has_index_col=True get_child_node="get_row"
vis . should_equal json
g = data.t.aggregate ["A", "B"] [Aggregate_Column.Average "C"] . at "Average C"
vis2 = Visualization.prepare_visualization g 1
json2 = make_json header=["Average C"] data=[[4.0]] all_rows=2 value_type=[value_type_float] has_index_col=True
json2 = make_json header=["Average C"] data=[[4.0]] all_rows=2 value_type=[value_type_float] has_index_col=True get_child_node="get_row"
vis2 . should_equal json2
group_builder.specify "should visualize dataframe tables" <|
vis = Visualization.prepare_visualization data.t2 1
value_type_int = JS_Object.from_pairs [["type", "Value_Type"], ["constructor", "Integer"], ["display_text", "Integer (64 bits)"], ["bits", 64]]
json = make_json header=["A", "B", "C"] data=[[1], [4], [7]] all_rows=3 value_type=[value_type_int, value_type_int, value_type_int] has_index_col=True
json = make_json header=["A", "B", "C"] data=[[1], [4], [7]] all_rows=3 value_type=[value_type_int, value_type_int, value_type_int] has_index_col=True get_child_node="get_row"
vis . should_equal json
group_builder.specify "should visualize dataframe columns" <|
vis = Visualization.prepare_visualization (data.t2.at "A") 2
value_type_int = JS_Object.from_pairs [["type", "Value_Type"], ["constructor", "Integer"], ["display_text", "Integer (64 bits)"], ["bits", 64]]
json = make_json header=["A"] data=[[1, 2]] all_rows=3 value_type=[value_type_int] has_index_col=True
json = make_json header=["A"] data=[[1, 2]] all_rows=3 value_type=[value_type_int] has_index_col=True get_child_node="get_row"
vis . should_equal json
group_builder.specify "should handle Vectors" <|
vis = Visualization.prepare_visualization [1, 2, 3] 2
json = JS_Object.from_pairs [["type", "Vector"], ["all_rows_count", 3], ["json", [1, 2]]]
json = JS_Object.from_pairs [["type", "Vector"], ["all_rows_count", 3], ["json", [1, 2]], ["get_child_node", "at"]]
vis . should_equal json.to_text
vis2 = Visualization.prepare_visualization [[1, 2], [3, 4]] 2
json2 = JS_Object.from_pairs [["type", "Matrix"], ["all_rows_count", 2], ["json", [[1, 2], [3, 4]]], ["column_count", 2]]
json2 = JS_Object.from_pairs [["type", "Matrix"], ["all_rows_count", 2], ["json", [[1, 2], [3, 4]]], ["column_count", 2], ["get_child_node", "at"]]
vis2 . should_equal json2.to_text
group_builder.specify "should handle Arrays" <|
vis = Visualization.prepare_visualization ([1, 2, 3] . to_array) 2
json = JS_Object.from_pairs [["type", "Vector"], ["all_rows_count", 3], ["json", [1, 2]]]
json = JS_Object.from_pairs [["type", "Vector"], ["all_rows_count", 3], ["json", [1, 2]], ["get_child_node", "at"]]
vis . should_equal json.to_text
group_builder.specify "should handle other datatypes" <|