terrier-engine 4.42.0 → 4.44.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -416,6 +416,7 @@ class DuplicateQueryModal extends ModalPart<DuplicateQueryState> {
416
416
  this.state.editor.queryTabs.showTab(query.id)
417
417
  this.pop()
418
418
  this.app.successToast("Duplicated Query", 'glyp-copy')
419
+ this.emitMessage(DiveEditor.diveChangedKey, {})
419
420
  }
420
421
 
421
422
  renderContent(parent: PartTag): void {
@@ -1,3 +1,4 @@
1
+ import Arrays from "tuff-core/arrays"
1
2
  import {ModalPart} from "../../terrier/modals"
2
3
  import {PartTag} from "tuff-core/parts"
3
4
  import {DdDive, DdDiveRun, UnpersistedDdDiveRun} from "../gen/models"
@@ -191,8 +192,12 @@ export class DiveRunModal extends ModalPart<{dive: DdDive }> {
191
192
  col.div('.tt-flex.collapsible.gap.tt-form', row => {
192
193
  // inputs
193
194
  row.div('.tt-flex.column.dd-dive-run-inputs', col => {
194
- for (const filter of this.filters) {
195
- this.renderInput(col, filter)
195
+ const filtersByQuery = Arrays.groupBy(this.filters, 'query_name')
196
+ for (const queryName of Object.keys(filtersByQuery).sort()) {
197
+ col.h3(".glyp-data_dive_query").text(queryName)
198
+ for (const filter of filtersByQuery[queryName]) {
199
+ this.renderInput(col, filter)
200
+ }
196
201
  }
197
202
  })
198
203
 
@@ -250,7 +255,7 @@ export class DiveRunModal extends ModalPart<{dive: DdDive }> {
250
255
  }
251
256
 
252
257
  renderFileOutput(parent: DivTag, fileOutput: RunFileOutput) {
253
- parent.a('.file-output', {href: fileOutput.url}, row => {
258
+ parent.a('.file-output', { href: fileOutput.url, download: fileOutput.name }, row => {
254
259
  row.div('.name.glyp-file_spreadsheet.with-icon').text(fileOutput.name)
255
260
  row.div('.details.glyp-download.with-icon').text("Click to download")
256
261
  })
@@ -61,7 +61,7 @@ async function list(): Promise<DiveListResult> {
61
61
  function computeFilterInputs(schema: SchemaDef, dive: DdDive): FilterInput[] {
62
62
  const filters: Record<string, FilterInput> = {}
63
63
  for (const query of dive.query_data?.queries || []) {
64
- Tables.computeFilterInputs(schema, query.from, filters)
64
+ Tables.computeFilterInputs(schema, query, query.from, filters)
65
65
  }
66
66
  return Object.values(filters)
67
67
  }
@@ -1,6 +1,7 @@
1
1
  import {PartTag} from "tuff-core/parts"
2
2
  import Dates, {DateLiteral, VirtualDatePeriod, VirtualDateRange} from "./dates"
3
3
  import {ColumnDef, ModelDef, SchemaDef} from "../../terrier/schema"
4
+ import { Query } from "./queries"
4
5
  import {TableRef, TableView} from "./tables"
5
6
  import {Logger} from "tuff-core/logging"
6
7
  import * as inflection from "inflection"
@@ -683,6 +684,7 @@ class AddFilterDropdown extends Dropdown<{modelDef: ModelDef, callback: AddFilte
683
684
  ////////////////////////////////////////////////////////////////////////////////
684
685
 
685
686
  export type FilterInput = Filter & {
687
+ query_name: string
686
688
  input_name: string
687
689
  input_value: string
688
690
  possible_values?: string[]
@@ -694,12 +696,12 @@ export type FilterInput = Filter & {
694
696
  * @param table
695
697
  * @param filter
696
698
  */
697
- function toInput(schema: SchemaDef, table: TableRef, filter: Filter): FilterInput {
699
+ function toInput(schema: SchemaDef, query: Query, table: TableRef, filter: Filter): FilterInput {
698
700
  if (!filter.id?.length) {
699
701
  filter.id = Ids.makeRandom(8)
700
702
  }
701
703
  const name = `${table.model}.${filter.column}`
702
- const filterInput: FilterInput = {...filter, input_name: name, input_value: ''}
704
+ const filterInput: FilterInput = {...filter, query_name: query.name, input_name: name, input_value: ''}
703
705
  switch (filter.filter_type) {
704
706
  case 'inclusion':
705
707
  const modelDef = schema.models[table.model]
@@ -1,3 +1,4 @@
1
+ import { Filter } from "./filters"
1
2
  import {TableRef} from "./tables"
2
3
  import api, {ApiResponse} from "../../terrier/api"
3
4
  import {PartTag} from "tuff-core/parts"
@@ -63,7 +64,7 @@ function eachTable(query: Query, fn: TableFunction) {
63
64
  eachChildTable(query.from, fn)
64
65
  }
65
66
 
66
- type ColumnFunction = (table: TableRef, col: ColumnRef) => any
67
+ export type ColumnFunction = (table: TableRef, col: ColumnRef) => any
67
68
 
68
69
  function eachColumnForTable(table: TableRef, fn: ColumnFunction) {
69
70
  if (table.columns) {
@@ -87,14 +88,44 @@ function eachColumn(query: Query, fn: ColumnFunction) {
87
88
  eachColumnForTable(query.from, fn)
88
89
  }
89
90
 
91
+ export type FilterFunction = (table: TableRef, filter: Filter) => any
92
+
93
+ function eachFilterForTable(table: TableRef, fn: FilterFunction) {
94
+ if (table.filters) {
95
+ for (const filter of table.filters) {
96
+ fn(table, filter)
97
+ }
98
+ }
99
+ if (table.joins) {
100
+ for (const joinedTable of Object.values(table.joins)) {
101
+ eachFilterForTable(joinedTable, fn)
102
+ }
103
+ }
104
+ }
105
+
90
106
  /**
91
- * Duplicates a query, including all of the nested data structures.
107
+ * Recursively iterates over all filters in the query and executes the given function for each.
108
+ * @param query
109
+ * @param fn a function to evaluate on each filter
110
+ */
111
+ function eachFilter(query: Query, fn: FilterFunction) {
112
+ eachFilterForTable(query.from, fn)
113
+ }
114
+
115
+ /**
116
+ * Duplicates a query, including all the nested data structures.
92
117
  * @param query the query to duplicate
93
118
  * @return a completely new query
94
119
  */
95
120
  function duplicate(query: Query): Query {
96
121
  const newQuery = Objects.deepCopy(query)
97
122
  newQuery.id = Ids.makeUuid()
123
+
124
+ // filters need new IDs, otherwise they won't be able to be set differently than the original query's filters
125
+ eachFilter(newQuery, (_, filter) => {
126
+ filter.id = Ids.makeUuid()
127
+ })
128
+
98
129
  return newQuery
99
130
  }
100
131
 
@@ -332,6 +363,7 @@ export class QueryModelPicker extends TerrierPart<QueryModelPickerState> {
332
363
  const Queries = {
333
364
  eachColumn,
334
365
  eachTable,
366
+ eachFilter,
335
367
  duplicate,
336
368
  validate,
337
369
  preview,
@@ -1,4 +1,5 @@
1
1
  import {ModalPart} from "../../terrier/modals"
2
+ import Columns from "./columns"
2
3
  import Queries, {OrderBy, Query} from "./queries"
3
4
  import Messages from "tuff-core/messages"
4
5
  import {PartTag} from "tuff-core/parts"
@@ -54,15 +55,19 @@ export default class RowOrderModal extends ModalPart<RowOrderState> {
54
55
 
55
56
  // collect the column options
56
57
  const query = this.state.query
57
- Queries.eachColumn(query, (_, col) => {
58
- this.columnOptions.push({title: col.name, value: col.name})
58
+ const existingColumns = new Set<string>()
59
+ Queries.eachColumn(query, (table, col) => {
60
+ const name = Columns.computeSelectName(table, col)
61
+ existingColumns.add(name)
62
+ this.columnOptions.push({title: name, value: name})
59
63
  })
60
64
 
61
- // initialize the order=bys from the query, if present
65
+ // initialize the order-bys from the query, if present
62
66
  if (query.order_by?.length) {
63
- this.orderBys = query.order_by
67
+ // filter out columns that are no longer present in the query
68
+ this.orderBys = query.order_by.filter(ob => existingColumns.has(ob.column))
64
69
  }
65
- else {
70
+ if (!query.order_by?.length) { // this isn't an else because the previous clause might've resulted in no columns
66
71
  // start with something by default
67
72
  this.addClause()
68
73
  }
@@ -47,22 +47,19 @@ const updatedKey = Messages.typedKey<TableRef>()
47
47
  ////////////////////////////////////////////////////////////////////////////////
48
48
 
49
49
  /**
50
- * Recursively collect s all of the filters for this and all joined tables.
51
- * Only keep one (the last one traversed) per table/column combination.
52
- * This means that some filters may clobber others, but I think it will yield
53
- * the desired result most of the time.
50
+ * Recursively collects all the filters for this and all joined tables.
54
51
  * @param schema
55
52
  * @param table
56
53
  * @param filters
57
54
  */
58
- function computeFilterInputs(schema: SchemaDef, table: TableRef, filters: Record<string, FilterInput>) {
55
+ function computeFilterInputs(schema: SchemaDef, query: Query, table: TableRef, filters: Record<string, FilterInput>) {
59
56
  for (const f of table.filters || []) {
60
- const fi = Filters.toInput(schema, table, f)
57
+ const fi = Filters.toInput(schema, query, table, f)
61
58
  filters[fi.id] = fi
62
59
  }
63
60
  if (table.joins) {
64
61
  for (const j of Object.values(table.joins)) {
65
- computeFilterInputs(schema, j, filters)
62
+ computeFilterInputs(schema, query, j, filters)
66
63
  }
67
64
  }
68
65
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "files": [
5
5
  "*"
6
6
  ],
7
- "version": "4.42.0",
7
+ "version": "4.44.2",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "https://github.com/Terrier-Tech/terrier-engine"