@graphenedata/cli 0.0.15 → 0.0.16
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.
- package/README.md +138 -0
- package/dist/cli/bigQuery-I3F46SC6.js +75 -0
- package/dist/cli/bigQuery-I3F46SC6.js.map +7 -0
- package/dist/cli/chunk-OVWODUTJ.js +12849 -0
- package/dist/cli/chunk-OVWODUTJ.js.map +7 -0
- package/dist/cli/chunk-QAXEOZ43.js +53 -0
- package/dist/cli/chunk-QAXEOZ43.js.map +7 -0
- package/dist/cli/cli.js +234 -11197
- package/dist/cli/clickhouse-ZN5AN2UL.js +64 -0
- package/dist/cli/clickhouse-ZN5AN2UL.js.map +7 -0
- package/dist/cli/duckdb-IYBIO5KJ.js +87 -0
- package/dist/cli/duckdb-IYBIO5KJ.js.map +7 -0
- package/dist/cli/serve2-TNN5EROW.js +447 -0
- package/dist/cli/serve2-TNN5EROW.js.map +7 -0
- package/dist/cli/snowflake-MOQB5GA4.js +128 -0
- package/dist/cli/snowflake-MOQB5GA4.js.map +7 -0
- package/dist/index.d.ts +63 -0
- package/dist/lang/index.d.ts +63 -0
- package/dist/skills/graphene/SKILL.md +150 -96
- package/dist/skills/graphene/references/big-value.md +6 -41
- package/dist/skills/graphene/references/date-range.md +64 -0
- package/dist/skills/graphene/references/dropdown.md +3 -4
- package/dist/skills/graphene/references/echarts.md +162 -0
- package/dist/skills/graphene/references/gsql.md +55 -25
- package/dist/skills/graphene/references/model-gsql.md +72 -0
- package/dist/skills/graphene/references/table.md +13 -14
- package/dist/skills/graphene/references/text-input.md +2 -1
- package/dist/ui/app.css +239 -340
- package/dist/ui/component-utilities/dataShaping.ts +484 -0
- package/dist/ui/component-utilities/dataSummary.ts +57 -0
- package/dist/ui/component-utilities/enrich.ts +763 -0
- package/dist/ui/component-utilities/format.ts +177 -0
- package/dist/ui/component-utilities/inputUtils.ts +44 -8
- package/dist/ui/component-utilities/theme.ts +200 -0
- package/dist/ui/component-utilities/themeStores.ts +21 -8
- package/dist/ui/component-utilities/types.ts +70 -0
- package/dist/ui/components/AreaChart.svelte +57 -105
- package/dist/ui/components/BarChart.svelte +71 -129
- package/dist/ui/components/BigValue.svelte +24 -40
- package/dist/ui/components/Column.svelte +10 -18
- package/dist/ui/components/DateRange.svelte +54 -21
- package/dist/ui/components/Dropdown.svelte +47 -26
- package/dist/ui/components/DropdownOption.svelte +1 -2
- package/dist/ui/components/ECharts.svelte +181 -67
- package/dist/ui/components/InlineDelta.svelte +50 -31
- package/dist/ui/components/LineChart.svelte +54 -125
- package/dist/ui/components/PieChart.svelte +27 -37
- package/dist/ui/components/QueryLoad.svelte +77 -45
- package/dist/ui/components/Row.svelte +2 -1
- package/dist/ui/components/ScatterPlot.svelte +52 -0
- package/dist/ui/components/Skeleton.svelte +32 -0
- package/dist/ui/components/Table.svelte +3 -2
- package/dist/ui/components/TableGroupRow.svelte +28 -36
- package/dist/ui/components/TableHarness.svelte +32 -0
- package/dist/ui/components/TableHeader.svelte +34 -59
- package/dist/ui/components/TableRow.svelte +14 -38
- package/dist/ui/components/TableSubtotalRow.svelte +18 -21
- package/dist/ui/components/TableTotalRow.svelte +27 -37
- package/dist/ui/components/TextInput.svelte +13 -12
- package/dist/ui/components/Value.svelte +25 -0
- package/dist/ui/components/_Table.svelte +72 -70
- package/dist/ui/internal/ChartGallery.svelte +527 -0
- package/dist/ui/internal/ErrorDisplay.svelte +22 -97
- package/dist/ui/internal/LocalApp.svelte +80 -17
- package/dist/ui/internal/PageNavGroup.svelte +269 -0
- package/dist/ui/internal/Sidebar.svelte +178 -0
- package/dist/ui/internal/SidebarToggle.svelte +47 -0
- package/dist/ui/internal/StyleGallery.svelte +244 -0
- package/dist/ui/internal/clientCache.ts +2 -2
- package/dist/ui/internal/pageInputs.svelte.js +292 -0
- package/dist/ui/internal/queryEngine.ts +102 -117
- package/dist/ui/internal/runSocket.ts +32 -12
- package/dist/ui/internal/sidebar.svelte.js +18 -0
- package/dist/ui/internal/telemetry.ts +51 -16
- package/dist/ui/internal/types.d.ts +7 -0
- package/dist/ui/web.js +28 -11
- package/package.json +36 -38
- package/dist/skills/graphene/references/area-chart.md +0 -95
- package/dist/skills/graphene/references/bar-chart.md +0 -112
- package/dist/skills/graphene/references/line-chart.md +0 -108
- package/dist/skills/graphene/references/pie-chart.md +0 -29
- package/dist/skills/graphene/references/value-formats.md +0 -104
- package/dist/ui/component-utilities/autoFormatting.js +0 -280
- package/dist/ui/component-utilities/builtInFormats.js +0 -481
- package/dist/ui/component-utilities/chartContext.js +0 -12
- package/dist/ui/component-utilities/chartWindowDebug.js +0 -21
- package/dist/ui/component-utilities/checkInputs.js +0 -84
- package/dist/ui/component-utilities/convert.js +0 -15
- package/dist/ui/component-utilities/dateParsing.js +0 -56
- package/dist/ui/component-utilities/dropdownContext.ts +0 -1
- package/dist/ui/component-utilities/echarts.js +0 -252
- package/dist/ui/component-utilities/echartsThemes.js +0 -443
- package/dist/ui/component-utilities/formatTitle.js +0 -24
- package/dist/ui/component-utilities/formatting.js +0 -241
- package/dist/ui/component-utilities/getColumnExtents.js +0 -79
- package/dist/ui/component-utilities/getColumnSummary.js +0 -62
- package/dist/ui/component-utilities/getCompletedData.js +0 -122
- package/dist/ui/component-utilities/getDistinctCount.js +0 -7
- package/dist/ui/component-utilities/getDistinctValues.js +0 -15
- package/dist/ui/component-utilities/getSeriesConfig.js +0 -231
- package/dist/ui/component-utilities/getSortedData.js +0 -9
- package/dist/ui/component-utilities/getStackPercentages.js +0 -45
- package/dist/ui/component-utilities/getStackedData.js +0 -19
- package/dist/ui/component-utilities/getYAxisIndex.js +0 -15
- package/dist/ui/component-utilities/globalContexts.js +0 -1
- package/dist/ui/component-utilities/helpers/getCompletedData.helpers.js +0 -119
- package/dist/ui/component-utilities/replaceNulls.js +0 -16
- package/dist/ui/component-utilities/tableUtils.ts +0 -107
- package/dist/ui/component-utilities/tidyWithTypes.js +0 -9
- package/dist/ui/components/Area.svelte +0 -214
- package/dist/ui/components/Bar.svelte +0 -347
- package/dist/ui/components/Chart.svelte +0 -995
- package/dist/ui/components/Line.svelte +0 -227
- package/dist/ui/internal/NavSidebar.svelte +0 -396
- package/dist/ui/internal/theme.ts +0 -60
- package/dist/ui/public/inter-latin-ext.woff2 +0 -0
- package/dist/ui/public/inter-latin.woff2 +0 -0
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import ssf from 'ssf'
|
|
2
|
-
import {getContext} from 'svelte'
|
|
3
|
-
|
|
4
|
-
import {findImplicitAutoFormat, autoFormat, fallbackFormat, isAutoFormat} from './autoFormatting'
|
|
5
|
-
import {BUILT_IN_FORMATS} from './builtInFormats'
|
|
6
|
-
import {standardizeDateString} from './dateParsing'
|
|
7
|
-
import {CUSTOM_FORMATTING_SETTINGS_CONTEXT_KEY} from './globalContexts'
|
|
8
|
-
|
|
9
|
-
const AXIS_FORMATTING_CONTEXT = 'axis'
|
|
10
|
-
const VALUE_FORMATTING_CONTEXT = 'value'
|
|
11
|
-
|
|
12
|
-
export const getCustomFormats = () => {
|
|
13
|
-
try {
|
|
14
|
-
return getContext(CUSTOM_FORMATTING_SETTINGS_CONTEXT_KEY)?.getCustomFormats() || []
|
|
15
|
-
} catch {
|
|
16
|
-
return []
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @param {*} columnName the name of the column
|
|
22
|
-
* @returns a format object (built-in or custom) based on the column name if it matches the pattern column_${formatTag}, otherwise returns undefined
|
|
23
|
-
*/
|
|
24
|
-
export const lookupColumnFormat = (columnName, columnEvidenceType, columnUnitSummary) => {
|
|
25
|
-
let potentialFormatTag = maybeExtractFormatTag(columnName)
|
|
26
|
-
|
|
27
|
-
if (columnEvidenceType.evidenceType === 'string') {
|
|
28
|
-
return undefined
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (potentialFormatTag) {
|
|
32
|
-
let customFormats = getCustomFormats()
|
|
33
|
-
let matchingFormat = [...BUILT_IN_FORMATS, ...customFormats].find(format => format.formatTag?.toLowerCase() === potentialFormatTag?.toLowerCase?.())
|
|
34
|
-
if (matchingFormat) {
|
|
35
|
-
return matchingFormat
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let matchingImplicitAutoFormat = findImplicitAutoFormat(columnName, columnEvidenceType, columnUnitSummary)
|
|
40
|
-
if (matchingImplicitAutoFormat) {
|
|
41
|
-
return matchingImplicitAutoFormat
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return undefined
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Returns an Evidence format object to be used in the applyFormatting function
|
|
49
|
-
* @param {string} formatString string containing an Excel-style format code, or a format name matching a built-in or custom format
|
|
50
|
-
* @param {'number' | 'date' | 'boolean' | 'string'} [valueType] optional - a string representing the data type within the column that will be formatted ('number', 'date', 'boolean', or 'string)
|
|
51
|
-
* @returns a format object based on the formatString matching a built-in or custom format name, or a new custom format object containing an Excel-style format code
|
|
52
|
-
*/
|
|
53
|
-
export function getFormatObjectFromString(formatString, valueType = undefined) {
|
|
54
|
-
let potentialFormatTag = formatString
|
|
55
|
-
let customFormats = getCustomFormats()
|
|
56
|
-
let matchingFormat = [...BUILT_IN_FORMATS, ...customFormats].find(format => format.formatTag?.toLowerCase() === potentialFormatTag?.toLowerCase?.())
|
|
57
|
-
let newFormat = {}
|
|
58
|
-
if (matchingFormat) {
|
|
59
|
-
if (!valueType || matchingFormat.valueType === valueType) return matchingFormat
|
|
60
|
-
return {...matchingFormat, valueType}
|
|
61
|
-
} else {
|
|
62
|
-
newFormat = {
|
|
63
|
-
formatTag: 'custom',
|
|
64
|
-
formatCode: potentialFormatTag,
|
|
65
|
-
}
|
|
66
|
-
if (valueType) {
|
|
67
|
-
newFormat.valueType = valueType
|
|
68
|
-
}
|
|
69
|
-
return newFormat
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export const formatValue = (value, columnFormat = undefined, columnUnitSummary = undefined) => {
|
|
74
|
-
try {
|
|
75
|
-
return applyFormatting(value, columnFormat, columnUnitSummary, VALUE_FORMATTING_CONTEXT)
|
|
76
|
-
} catch (error) {
|
|
77
|
-
//fallback to default
|
|
78
|
-
console.warn(`Unexpected error calling applyFormatting(${value}, ${columnFormat}, ${VALUE_FORMATTING_CONTEXT}, ${columnUnitSummary}). Error=${error}`)
|
|
79
|
-
return value
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export const formatAxisValue = (value, columnFormat = undefined, columnUnitSummary = undefined) => {
|
|
84
|
-
try {
|
|
85
|
-
return applyFormatting(value, columnFormat, columnUnitSummary, AXIS_FORMATTING_CONTEXT)
|
|
86
|
-
} catch {
|
|
87
|
-
//fallback to default
|
|
88
|
-
}
|
|
89
|
-
return value
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export const applyTitleTagReplacement = (columnName, columnFormatSettings) => {
|
|
93
|
-
let result = columnName
|
|
94
|
-
if (columnName && columnFormatSettings?.formatTag) {
|
|
95
|
-
let lastIndexOfTag = columnName.toLowerCase().lastIndexOf(`_${columnFormatSettings.formatTag.toLowerCase()}`)
|
|
96
|
-
let titleTagReplacement = ''
|
|
97
|
-
if (lastIndexOfTag > 0) {
|
|
98
|
-
//explicitly ignore columns starting with _, hence >0 instead of => 0
|
|
99
|
-
if (typeof columnFormatSettings?.titleTagReplacement === 'string') {
|
|
100
|
-
titleTagReplacement = columnFormatSettings.titleTagReplacement
|
|
101
|
-
}
|
|
102
|
-
result = columnName.substring(0, lastIndexOfTag) + titleTagReplacement
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return result
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export const defaultExample = valueType => {
|
|
109
|
-
switch (valueType) {
|
|
110
|
-
case 'number':
|
|
111
|
-
return 1234
|
|
112
|
-
case 'date':
|
|
113
|
-
return '2022-01-03'
|
|
114
|
-
default:
|
|
115
|
-
return undefined
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export const formatExample = format => {
|
|
120
|
-
let normalizedUserInput = format.userInput?.trim()
|
|
121
|
-
let preFormattedValue = normalizedUserInput || format.exampleInput || defaultExample(format.valueType)
|
|
122
|
-
|
|
123
|
-
if (preFormattedValue) {
|
|
124
|
-
try {
|
|
125
|
-
let columnUnitSummary = undefined
|
|
126
|
-
if (format.valueType === 'number') {
|
|
127
|
-
let numericValue = Number(preFormattedValue)
|
|
128
|
-
columnUnitSummary = {
|
|
129
|
-
min: numericValue,
|
|
130
|
-
max: numericValue,
|
|
131
|
-
median: numericValue,
|
|
132
|
-
maxDecimals: numericValue.toString().split('.')[1]?.length || 0,
|
|
133
|
-
unitType: 'number',
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return applyFormatting(preFormattedValue, format, columnUnitSummary, VALUE_FORMATTING_CONTEXT)
|
|
137
|
-
} catch {
|
|
138
|
-
//return default value
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return ''
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function applyFormatting(value, columnFormat = undefined, columnUnitSummary = undefined, formattingContext = VALUE_FORMATTING_CONTEXT) {
|
|
145
|
-
if (value === undefined || value === null) {
|
|
146
|
-
return '-'
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
let result = undefined
|
|
150
|
-
if (columnFormat) {
|
|
151
|
-
try {
|
|
152
|
-
let formattingCode = getEffectiveFormattingCode(columnFormat, formattingContext)
|
|
153
|
-
let typedValue
|
|
154
|
-
try {
|
|
155
|
-
if (columnFormat.valueType === 'date' && typeof value === 'string') {
|
|
156
|
-
typedValue = new Date(standardizeDateString(value))
|
|
157
|
-
} else if (value instanceof Date) {
|
|
158
|
-
// "2023-09-06T22:40:43.000Z" minus the Z is interpreted
|
|
159
|
-
// as local time
|
|
160
|
-
// similar in behavior to standardizeDateString
|
|
161
|
-
typedValue = new Date(value.toISOString().slice(0, -1))
|
|
162
|
-
} else if (columnFormat.valueType === 'number' && typeof value !== 'number' && !Number.isNaN(value)) {
|
|
163
|
-
typedValue = Number(value)
|
|
164
|
-
} else {
|
|
165
|
-
typedValue = value
|
|
166
|
-
}
|
|
167
|
-
} catch {
|
|
168
|
-
typedValue = value
|
|
169
|
-
}
|
|
170
|
-
if (isAutoFormat(columnFormat, formattingCode)) {
|
|
171
|
-
try {
|
|
172
|
-
result = autoFormat(typedValue, columnFormat, columnUnitSummary)
|
|
173
|
-
} catch (error) {
|
|
174
|
-
console.warn(`Unexpected error applying auto formatting. Error=${error}`)
|
|
175
|
-
}
|
|
176
|
-
} else if (columnFormat.valueType === 'number' && typeof typedValue === 'number' && isYearOnlyFormat(formattingCode)) {
|
|
177
|
-
result = formatNumericYear(typedValue, formattingCode)
|
|
178
|
-
} else {
|
|
179
|
-
result = ssf.format(formattingCode, typedValue)
|
|
180
|
-
}
|
|
181
|
-
} catch (error) {
|
|
182
|
-
console.warn(`Unexpected error applying formatting ${error}`)
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
if (result === undefined) {
|
|
186
|
-
result = fallbackFormat(value, columnUnitSummary)
|
|
187
|
-
}
|
|
188
|
-
return result
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function isYearOnlyFormat(formattingCode) {
|
|
192
|
-
return typeof formattingCode === 'string' && /^y{2,4}$/i.test(formattingCode.trim())
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function formatNumericYear(value, formattingCode) {
|
|
196
|
-
let year = String(Math.trunc(value))
|
|
197
|
-
let width = formattingCode.trim().length
|
|
198
|
-
if (width === 2) {
|
|
199
|
-
return year.slice(-2).padStart(2, '0')
|
|
200
|
-
}
|
|
201
|
-
return year.padStart(width, '0')
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function getEffectiveFormattingCode(columnFormat, formattingContext = VALUE_FORMATTING_CONTEXT) {
|
|
205
|
-
if (typeof columnFormat === 'string') {
|
|
206
|
-
// This should only be used by end users, not by components.
|
|
207
|
-
return columnFormat
|
|
208
|
-
} else {
|
|
209
|
-
if (formattingContext === AXIS_FORMATTING_CONTEXT && columnFormat?.axisFormatCode) {
|
|
210
|
-
return columnFormat.axisFormatCode
|
|
211
|
-
}
|
|
212
|
-
return columnFormat?.formatCode
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Extracts a possible format tag from a column name based on the column name pattern
|
|
218
|
-
* @returns "column_${formatTag}" will return ${formatTag} or undefined if the columnName doesn't match the pattern
|
|
219
|
-
*/
|
|
220
|
-
function maybeExtractFormatTag(columnName) {
|
|
221
|
-
let normalizedColName = columnName.toLowerCase()
|
|
222
|
-
let lastUnderScoreIndex = normalizedColName.lastIndexOf('_')
|
|
223
|
-
|
|
224
|
-
if (lastUnderScoreIndex > 0) {
|
|
225
|
-
return normalizedColName.substr(lastUnderScoreIndex).replace('_', '')
|
|
226
|
-
} else {
|
|
227
|
-
return undefined
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Formats a value to whichever format is passed in
|
|
233
|
-
* @param {*} value the value to be formatted
|
|
234
|
-
* @param {string} format string containing an Excel-style format code, or a format name matching a built-in or custom format
|
|
235
|
-
* @param {'number' | 'date' | 'boolean' | 'string'} [valueType] the known column type
|
|
236
|
-
* @returns a formatted value
|
|
237
|
-
*/
|
|
238
|
-
export function fmt(value, format, valueType = undefined) {
|
|
239
|
-
let formatObj = getFormatObjectFromString(format, valueType)
|
|
240
|
-
return formatValue(value, formatObj)
|
|
241
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import {tidy, summarize, min, max, median, mean, n, nDistinct, sum} from '@tidyjs/tidy'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
* @param {Record<string, unknown>[]} data
|
|
6
|
-
* @param {string} columnName
|
|
7
|
-
* @param {boolean} [isNumeric]
|
|
8
|
-
* @returns {{ min?: number, max?: number, median?: number, mean?: number, count?: number, countDistinct?: number, sum?: number, maxDecimals: number, unitType: string }}
|
|
9
|
-
*/
|
|
10
|
-
export function getColumnUnitSummary(data, columnName, isNumeric = true) {
|
|
11
|
-
let seriesExtents = tidy(
|
|
12
|
-
data,
|
|
13
|
-
isNumeric
|
|
14
|
-
? summarize({
|
|
15
|
-
count: n(columnName),
|
|
16
|
-
countDistinct: nDistinct(columnName),
|
|
17
|
-
min: min(columnName),
|
|
18
|
-
max: max(columnName),
|
|
19
|
-
median: median(columnName),
|
|
20
|
-
mean: mean(columnName),
|
|
21
|
-
sum: sum(columnName),
|
|
22
|
-
})
|
|
23
|
-
: summarize({count: n(columnName), countDistinct: nDistinct(columnName)}),
|
|
24
|
-
)[0]
|
|
25
|
-
|
|
26
|
-
// TODO try to use summarize spec in tidy
|
|
27
|
-
let {maxDecimals, unitType} = summarizeUnits(data.map(row => row[columnName]))
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
min: seriesExtents.min,
|
|
31
|
-
max: seriesExtents.max,
|
|
32
|
-
median: seriesExtents.median,
|
|
33
|
-
mean: seriesExtents.mean,
|
|
34
|
-
count: seriesExtents.count,
|
|
35
|
-
countDistinct: seriesExtents.countDistinct,
|
|
36
|
-
sum: seriesExtents.sum,
|
|
37
|
-
maxDecimals: maxDecimals,
|
|
38
|
-
unitType: unitType,
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
44
|
-
* @param {Record<string, unknown>[]} data
|
|
45
|
-
* @param {string} column
|
|
46
|
-
* @returns {[number?, number?]}
|
|
47
|
-
*/
|
|
48
|
-
export function getColumnExtentsLegacy(data, column) {
|
|
49
|
-
let domainData = tidy(data, summarize({min: min(column), max: max(column)}))[0]
|
|
50
|
-
return [domainData.min, domainData.max]
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
*
|
|
55
|
-
* @param {number[]} series
|
|
56
|
-
* @returns {{ maxDecimals: number, unitType: string }}
|
|
57
|
-
*/
|
|
58
|
-
function summarizeUnits(series) {
|
|
59
|
-
if (series === undefined || series === null || series.length === 0) {
|
|
60
|
-
return {
|
|
61
|
-
maxDecimals: 0,
|
|
62
|
-
unitType: 'unknown',
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
let maxDecimals = 0
|
|
66
|
-
|
|
67
|
-
for (let element of series) {
|
|
68
|
-
let decimal_places = element?.toString().split('.')[1]?.length
|
|
69
|
-
if (decimal_places > maxDecimals) {
|
|
70
|
-
maxDecimals = decimal_places
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
maxDecimals: maxDecimals,
|
|
76
|
-
unitType: 'number',
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import {lookupColumnFormat} from './formatting.js'
|
|
2
|
-
import formatTitle from './formatTitle.js'
|
|
3
|
-
import {getColumnUnitSummary} from './getColumnExtents.js'
|
|
4
|
-
|
|
5
|
-
const EvidenceType = {
|
|
6
|
-
BOOLEAN: 'boolean',
|
|
7
|
-
NUMBER: 'number',
|
|
8
|
-
STRING: 'string',
|
|
9
|
-
DATE: 'date',
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @typedef {Object} ColumnSummary
|
|
14
|
-
* @property {string} title
|
|
15
|
-
* @property {string} type
|
|
16
|
-
* @property {Object} evidenceColumnType
|
|
17
|
-
* @property {ReturnType<typeof lookupColumnFormat>} format
|
|
18
|
-
* @property {ReturnType<typeof getColumnUnitSummary>} columnUnitSummary
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @function
|
|
23
|
-
* @template T
|
|
24
|
-
* @param {Record<string, unknown>[]} data
|
|
25
|
-
* @param {T} returnType
|
|
26
|
-
* @returns {T extends 'object' ? Record<string, ColumnSummary> : (ColumnSummary & { id: string })[]}
|
|
27
|
-
*/
|
|
28
|
-
export default function getColumnSummary(data, returnType = 'object') {
|
|
29
|
-
/** @type {Record<string, ColumnSummary>} */
|
|
30
|
-
let columnSummary = {}
|
|
31
|
-
|
|
32
|
-
let types = Array.isArray(data?._evidenceColumnTypes) ? data._evidenceColumnTypes : []
|
|
33
|
-
|
|
34
|
-
for (let colName of Object.keys(data[0])) {
|
|
35
|
-
let evidenceColumnType = types.find(item => item.name?.toLowerCase() === colName?.toLowerCase()) ?? {
|
|
36
|
-
name: colName,
|
|
37
|
-
evidenceType: EvidenceType.STRING,
|
|
38
|
-
}
|
|
39
|
-
let type = evidenceColumnType.evidenceType
|
|
40
|
-
let columnUnitSummary = evidenceColumnType.evidenceType === EvidenceType.NUMBER ? getColumnUnitSummary(data, colName, true) : getColumnUnitSummary(data, colName, false)
|
|
41
|
-
|
|
42
|
-
if (evidenceColumnType.evidenceType !== EvidenceType.NUMBER) {
|
|
43
|
-
columnUnitSummary.maxDecimals = 0
|
|
44
|
-
columnUnitSummary.unitType = evidenceColumnType.evidenceType
|
|
45
|
-
}
|
|
46
|
-
let format = lookupColumnFormat(colName, evidenceColumnType, columnUnitSummary)
|
|
47
|
-
|
|
48
|
-
columnSummary[colName] = {
|
|
49
|
-
title: formatTitle(colName, format),
|
|
50
|
-
type,
|
|
51
|
-
evidenceColumnType,
|
|
52
|
-
format,
|
|
53
|
-
columnUnitSummary,
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (returnType !== 'object') {
|
|
58
|
-
return Object.entries(columnSummary).map(([key, value]) => ({id: key, ...value}))
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return columnSummary
|
|
62
|
-
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import {tidy, complete} from '@tidyjs/tidy'
|
|
2
|
-
|
|
3
|
-
import getDistinctValues from './getDistinctValues'
|
|
4
|
-
import {findInterval, vectorSeq} from './helpers/getCompletedData.helpers.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* This function fills missing data points in the given data array for a specific series.
|
|
8
|
-
*
|
|
9
|
-
* @param {Record<string, unknown>[]} _data - The data as an array of objects.
|
|
10
|
-
* @param {string} x - The property used as x-axis.
|
|
11
|
-
* @param {string} y - The property used as y-axis.
|
|
12
|
-
* @param {string} series - The specific series in the data to be filled.
|
|
13
|
-
* @param {boolean} [nullsZero=false] - A flag indicating whether nulls should be replaced with zero.
|
|
14
|
-
* @param {boolean} [fillX=false] - A flag indicating whether the x-axis values should be filled (based on the found interval distance).
|
|
15
|
-
* @return {Record<string, unknown>[]} An array containing the filled data objects.
|
|
16
|
-
*/
|
|
17
|
-
export default function getCompletedData(_data, x, y, series, nullsZero = false, fillX = false) {
|
|
18
|
-
let xIsDate = false
|
|
19
|
-
let data = _data
|
|
20
|
-
.map(d =>
|
|
21
|
-
Object.assign({}, d, {
|
|
22
|
-
[x]: d[x] instanceof Date ? ((xIsDate = true), d[x].toISOString()) : d[x],
|
|
23
|
-
}),
|
|
24
|
-
)
|
|
25
|
-
.filter(d => d[x] !== undefined && d[x] !== null)
|
|
26
|
-
let groups = Array.from(data).reduce((a, v) => {
|
|
27
|
-
if (v[x] instanceof Date) {
|
|
28
|
-
v[x] = v[x].toISOString()
|
|
29
|
-
xIsDate = true
|
|
30
|
-
}
|
|
31
|
-
if (series) {
|
|
32
|
-
if (!a[v[series] ?? 'null']) a[v[series] ?? 'null'] = []
|
|
33
|
-
a[v[series] ?? 'null'].push(v)
|
|
34
|
-
} else {
|
|
35
|
-
if (!a.default) a.default = []
|
|
36
|
-
a.default.push(v)
|
|
37
|
-
}
|
|
38
|
-
return a
|
|
39
|
-
}, {})
|
|
40
|
-
|
|
41
|
-
// Ensures that all permutations of this map exist in the output
|
|
42
|
-
// e.g. can include series and x values to ensure that all series have all x values
|
|
43
|
-
let expandKeys = {}
|
|
44
|
-
|
|
45
|
-
/** @type {Array<number | string>} */
|
|
46
|
-
let xDistinct
|
|
47
|
-
|
|
48
|
-
let exampleX = data.find(item => item && item[x] !== null && item[x] !== undefined)?.[x] ?? null
|
|
49
|
-
// const exampleX = data[0]?.[x];
|
|
50
|
-
switch (typeof exampleX) {
|
|
51
|
-
case 'object':
|
|
52
|
-
if (exampleX === null) {
|
|
53
|
-
throw new Error(`Column '${x}' is entirely null. Column must contain at least one non-null value.`)
|
|
54
|
-
} else {
|
|
55
|
-
throw new Error('Unexpected object property, expected string, date, or number')
|
|
56
|
-
}
|
|
57
|
-
case 'number':
|
|
58
|
-
// Numbers are the most straightforward
|
|
59
|
-
xDistinct = getDistinctValues(data, x)
|
|
60
|
-
if (fillX) {
|
|
61
|
-
// Attempt to derive the interval between X values and interpolate missing values in that set (within the bounds of min/max)
|
|
62
|
-
let interval = findInterval(xDistinct)
|
|
63
|
-
expandKeys[x] = vectorSeq(xDistinct, interval)
|
|
64
|
-
}
|
|
65
|
-
break
|
|
66
|
-
case 'string':
|
|
67
|
-
xDistinct = getDistinctValues(data, x)
|
|
68
|
-
expandKeys[x] = xDistinct
|
|
69
|
-
break
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let output = []
|
|
73
|
-
|
|
74
|
-
for (let value of Object.values(groups)) {
|
|
75
|
-
let nullySpec = series ? {[series]: null} : {}
|
|
76
|
-
if (nullsZero) {
|
|
77
|
-
if (y instanceof Array) {
|
|
78
|
-
for (let i = 0; i < y.length; i++) {
|
|
79
|
-
nullySpec[y[i]] = 0
|
|
80
|
-
}
|
|
81
|
-
} else {
|
|
82
|
-
nullySpec[y] = 0
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
// Ensure null for consistency
|
|
86
|
-
if (y instanceof Array) {
|
|
87
|
-
for (let i = 0; i < y.length; i++) {
|
|
88
|
-
nullySpec[y[i]] = null
|
|
89
|
-
}
|
|
90
|
-
} else {
|
|
91
|
-
nullySpec[y] = null
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (series) {
|
|
96
|
-
expandKeys[series] = series
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
let tidyFuncs = []
|
|
100
|
-
if (Object.keys(expandKeys).length === 0) {
|
|
101
|
-
// empty object, no special configuration
|
|
102
|
-
tidyFuncs.push(complete([x], nullySpec))
|
|
103
|
-
} else {
|
|
104
|
-
tidyFuncs.push(complete(expandKeys, nullySpec))
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
output.push(tidy(value, ...tidyFuncs))
|
|
108
|
-
}
|
|
109
|
-
if (xIsDate) {
|
|
110
|
-
let converted = output.flat().map(r => ({...r, [x]: new Date(r[x])}))
|
|
111
|
-
if (Array.isArray(_data?._evidenceColumnTypes)) {
|
|
112
|
-
converted._evidenceColumnTypes = _data._evidenceColumnTypes
|
|
113
|
-
}
|
|
114
|
-
return converted
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
let flattened = output.flat()
|
|
118
|
-
if (Array.isArray(_data?._evidenceColumnTypes)) {
|
|
119
|
-
flattened._evidenceColumnTypes = _data._evidenceColumnTypes
|
|
120
|
-
}
|
|
121
|
-
return flattened
|
|
122
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extracts an array of distinct values from a specified column in a dataset.
|
|
3
|
-
*
|
|
4
|
-
* This function iterates over the dataset, collecting values from the specified
|
|
5
|
-
* column into a Set to ensure uniqueness. It then converts the Set into an array
|
|
6
|
-
* of distinct values and returns this array.
|
|
7
|
-
*
|
|
8
|
-
* @param {Object[]} data - The dataset to process, represented as an array of objects.
|
|
9
|
-
* @param {string} column - The name of the column from which to extract distinct values.
|
|
10
|
-
* @returns {any[]} An array containing distinct values from the specified column of the dataset.
|
|
11
|
-
*/
|
|
12
|
-
export default function getDistinctValues(data, column) {
|
|
13
|
-
let set = new Set(data.map(val => val[column]))
|
|
14
|
-
return Array.from(set)
|
|
15
|
-
}
|