-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathTestAppProfile.ts
218 lines (205 loc) · 6.26 KB
/
TestAppProfile.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*!
* Copyright (c) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE file in the project.
*/
import type {
AppProfile,
AppProfileInitializationContext,
} from '@datashaper/app-framework'
import { CommandBarSection, ResourceGroupType } from '@datashaper/app-framework'
import type { FieldWellItem } from '@datashaper/react'
import type { ResourceSchema } from '@datashaper/schema'
import { KnownProfile, KnownRel } from '@datashaper/schema'
import type { DataPackage, Resource } from '@datashaper/workflow'
import { ResourceReference, isReference } from '@datashaper/workflow'
import type { SettingConfig, SettingsConfig } from '@essex/components'
import type { IContextualMenuItem, IDropdownOption } from '@fluentui/react'
import { DropdownMenuItemType } from '@fluentui/react'
import type { AppContext } from '../../types.js'
import { TestApp } from './TestApp.js'
import type { TestAppConfig } from './TestAppResource.js'
import { TestAppResource } from './TestAppResource.js'
import { TEST_APP_PROFILE } from './constants.js'
export class TestAppProfile
implements AppProfile<TestAppResource, ResourceSchema, AppContext>
{
public readonly profile = TEST_APP_PROFILE
public readonly title = 'Test App'
public readonly iconName = 'TestBeaker'
public readonly renderer = TestApp
public readonly group = ResourceGroupType.Apps
private _dataPackage: DataPackage | undefined
private _appContext: AppContext | undefined
public initialize({
dataPackage,
appContext,
}: AppProfileInitializationContext): void {
this._dataPackage = dataPackage
this._appContext = appContext as AppContext
}
public createInstance(
schema?: ResourceSchema | undefined,
): Promise<TestAppResource> {
const defaultSettings = getDefaultSettings(this.getSettingsConfig())
const result = new TestAppResource(defaultSettings)
if (schema != null) {
result.loadSchema(schema)
}
return Promise.resolve(result)
}
public getCommandBarCommands(
section: CommandBarSection,
): IContextualMenuItem[] | undefined {
const dp = this._dataPackage
if (dp == null) {
throw new Error('Data package not initialized')
}
if (section === CommandBarSection.New) {
return [
{
key: this.profile,
text: `New ${this.title}`,
onClick: () => {
this.createInstance().then((resource) => {
resource.name = dp.suggestResourceName(resource.name)
dp.addResource(resource)
})
},
},
]
}
}
public getFieldWells(resource: TestAppResource): FieldWellItem[] {
// list out all root siblings as base options
const siblings = this._dataPackage?.resources
// filter this full list to only include tables and not include the current resource
const options = createSourceFieldOptions(
this._dataPackage?.resources,
resource.sources,
(r) => r.profile === KnownProfile.TableBundle && r.name !== resource.name,
)
return [
{
key: 'input-table',
title: 'Input table',
icon: 'Table',
placeholder: 'Select input table',
selectedKey: resource.input,
disabled: options.length === 2, // if there's only 2 options, it's just the headers
options,
onChange: (key: string) => {
clearInput(resource)
linkInput(resource, siblings, key)
resource.input = key
},
onReset: () => {
// unwind the onChange logic
clearInput(resource)
resource.input = undefined
},
},
]
}
public getSettingsConfig(): SettingsConfig {
return {
title: {
defaultValue: this._appContext?.initialSettings.name,
},
version: {
defaultValue: 1,
},
language: {
defaultValue: 'JavaScript',
params: {
options: ['JavaScript', 'TypeScript', 'Python', 'Rust'],
},
},
metrics: {
defaultValue: ['accuracy'],
params: {
options: ['accuracy', 'precision', 'recall', 'f1'],
},
},
}
}
}
// iterate through the settings config and construct
// a basic object using the default values
function getDefaultSettings(config: SettingsConfig): TestAppConfig {
return (
Object.entries(config) as Array<[keyof TestAppConfig, SettingConfig]>
).reduce((acc, cur) => {
const [key, conf] = cur
acc[key] = conf.defaultValue
return acc
}, {} as any) as TestAppConfig // `any` here works around an issue with the empty initial object since all properties on TestAppConfig are required
}
// if the source is a sibling, create a symlink
// otherwise, it should already be a child
function linkInput(
resource: TestAppResource,
siblings: Resource[] | undefined,
key: string,
) {
const sibling = siblings?.find((r) => r?.name === key)
if (sibling) {
const reference = new ResourceReference()
reference.target = sibling
reference.rel = KnownRel.Input
resource.sources = [...resource.sources, reference]
}
}
// remove the previous input if relevant
// note: only actually remove it from the sources if it is a symlink
// if it is a child, it should only be unlinked as input, not removed
function clearInput(resource: TestAppResource) {
if (resource.input) {
resource.sources = resource.sources.filter((r) => {
if (!isReference(r)) {
return true
}
return r?.target?.name !== resource.input
})
}
}
/**
* Makes a set of valid options to select from for a field well dropdown.
* This combines all siblings (if provided), all children (if provided),
* and a predicate to add additional filtering logic.
* @param resources - the sibling resources in the data package
* @param sources - child resources of the resource containing the well
*/
function createSourceFieldOptions(
resources: Resource[] | undefined,
sources: (Resource | ResourceReference)[],
predicate: (r: Resource | ResourceReference) => boolean,
): IDropdownOption[] {
const siblings = (resources || [])
.filter(predicate)
.map((r) => resourceOption(r))
// list out all children that are not already links to roots
const children = sources
.filter((r) => !isReference(r))
.filter(predicate)
.map((r) => resourceOption(r))
return [
{
key: '__siblings__',
text: 'Package tables',
itemType: DropdownMenuItemType.Header,
},
...siblings,
{
key: '__children__',
text: 'Child tables',
itemType: DropdownMenuItemType.Header,
},
...children,
]
}
function resourceOption(resource: Resource): IDropdownOption {
return {
key: resource.name,
text: resource.title || resource.name,
}
}