@datagrok/hit-triage 1.9.2 → 1.10.0

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.
@@ -0,0 +1 @@
1
+ {"name":"Demo Template","key":"DMT","sequenceColumnName":"Helm","campaignFields":[{"name":"Chemist","type":"String","required":true},{"name":"Supervisor","type":"String","required":true}],"dataSourceType":"Query","compute":{"descriptors":{"enabled":true,"args":["MolWt","HeavyAtomMolWt"]},"functions":[{"name":"addChemPropertiesColumns","package":"Chem","args":{"MW":false,"HBA":true,"HBD":true,"logP":false,"logS":false,"PSA":false,"rotatableBonds":false,"stereoCenters":false,"moleculeCharge":false}}],"scripts":[],"queries":[]},"submit":{"fName":"demoFileSubmit","package":"HitTriage"},"queryFunctionName":"Demo Peptide Sequences"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/hit-triage",
3
3
  "friendlyName": "Hit Triage",
4
- "version": "1.9.2",
4
+ "version": "1.10.0",
5
5
  "author": {
6
6
  "name": "Davit Rizhinashvili",
7
7
  "email": "drizhinashvili@datagrok.ai"
@@ -17,6 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@datagrok-libraries/compute-utils": "^1.44.9",
20
+ "@datagrok-libraries/statistics": "^1.12.4",
20
21
  "@datagrok-libraries/utils": "^4.6.5",
21
22
  "cash-dom": "^8.1.5",
22
23
  "css-loader": "^6.5.1",
@@ -28,7 +29,8 @@
28
29
  "typeahead-standalone": "4.14.1",
29
30
  "typescript": "^5.6.3",
30
31
  "uuid": "^9.0.0",
31
- "@datagrok-libraries/test": "^1.1.0"
32
+ "@datagrok-libraries/test": "^1.1.0",
33
+ "@datagrok-libraries/bio": "^5.63.6"
32
34
  },
33
35
  "devDependencies": {
34
36
  "@types/uuid": "^9.0.2",
@@ -52,8 +54,8 @@
52
54
  "release-hittriage-dev": "grok publish dev --release",
53
55
  "debug-hittriage-public": "grok publish public",
54
56
  "release-hittriage-public": "grok publish public --release",
55
- "link-all": "npm link datagrok-api @datagrok-libraries/compute-utils @datagrok-libraries/utils",
56
- "build-all": "npm --prefix ./../../js-api run build && npm --prefix ./../../libraries/compute-utils run build && npm --prefix ./../../libraries/utils run build && npm run build"
57
+ "link-all": "npm link datagrok-api @datagrok-libraries/compute-utils @datagrok-libraries/statistics @datagrok-libraries/utils",
58
+ "build-all": "npm --prefix ./../../js-api run build && npm --prefix ./../../libraries/compute-utils run build && npm --prefix ./../../libraries/statistics run build && npm --prefix ./../../libraries/utils run build && npm run build"
57
59
  },
58
60
  "canEdit": [
59
61
  "Administrators"
@@ -7,10 +7,17 @@ import {CampaignFieldTypes, HitTriageTemplate, IngestType} from '../types';
7
7
  import * as C from '../consts';
8
8
  import '../../../css/hit-triage.css';
9
9
 
10
- type INewCampaignResult = {
10
+ export type INewCampaignResult = {
11
11
  df: DG.DataFrame,
12
12
  type: IngestType,
13
- campaignProps: {[key: string]: any}
13
+ campaignProps: {[key: string]: any},
14
+ friendlyName?: string,
15
+ extraData?: {[key: string]: any},
16
+ }
17
+
18
+ export type CampaignAccordeonOptions = {
19
+ extraFormElements?: HTMLElement[],
20
+ getExtraData?: () => {[key: string]: any},
14
21
  }
15
22
 
16
23
  type HitTriageCampaignAccordeon = {
@@ -24,7 +31,9 @@ type HitTriageCampaignAccordeon = {
24
31
  * @return {HitTriageCampaignAccordeon} Object containing root element, promise for the campaign result and cancel
25
32
  */
26
33
  export async function newCampaignAccordeon(template: HitTriageTemplate,
27
- dataSourceFunctionsMap: {[key: string]: DG.Func | DG.DataQuery}): Promise<HitTriageCampaignAccordeon> {
34
+ dataSourceFunctionsMap: {[key: string]: DG.Func | DG.DataQuery},
35
+ dataSourceTag: string = C.HitTriageDataSourceTag,
36
+ options?: CampaignAccordeonOptions): Promise<HitTriageCampaignAccordeon> {
28
37
  const errorDiv = ui.divText('', {style: {color: 'red'}});
29
38
  // handling file input
30
39
  let fileDf: DG.DataFrame | null = null;
@@ -52,7 +61,7 @@ export async function newCampaignAccordeon(template: HitTriageTemplate,
52
61
  if (Object.keys(dataSourceFunctionsMap).length === 0) {
53
62
  // functions that have special tag and are applicable for data source. they should return a dataframe with molecules
54
63
  const dataSourceFunctions =
55
- Array.from(new Set(DG.Func.find({meta: {role: C.HitTriageDataSourceTag}}).concat(DG.Func.find({tags: [C.HitTriageDataSourceTag]}))));
64
+ Array.from(new Set(DG.Func.find({meta: {role: dataSourceTag}}).concat(DG.Func.find({tags: [dataSourceTag]}))));
56
65
  // for display purposes we use friendly name of the function
57
66
  dataSourceFunctions.forEach((func) => {
58
67
  dataSourceFunctionsMap[func.friendlyName ?? func.name] = func;
@@ -82,6 +91,9 @@ export async function newCampaignAccordeon(template: HitTriageTemplate,
82
91
  functionInputDiv.style.display = 'none';
83
92
  const dataInputsDiv = ui.div([fileInputDiv, functionInputDiv]);
84
93
 
94
+ const campaignNameInput = ui.input.string('Campaign Name', {value: '', nullable: true});
95
+ campaignNameInput.setTooltip('Optional friendly name for the campaign');
96
+
85
97
  // campaign properties. each template might have number of additional fields that should
86
98
  // be filled by user for the campaign. they are cast into DG.Property objects and displayed as a form
87
99
  const campaignProps = template.campaignFields
@@ -104,6 +116,8 @@ export async function newCampaignAccordeon(template: HitTriageTemplate,
104
116
  }
105
117
 
106
118
  const form = ui.div([
119
+ campaignNameInput.root,
120
+ ...(options?.extraFormElements ?? []),
107
121
  dataInputsDiv,
108
122
  ...(campaignProps.length ? [campaignPropsForm] : [])]);
109
123
  const buttonsDiv = ui.buttonsInput([]); // div for create and cancel buttons
@@ -117,7 +131,9 @@ export async function newCampaignAccordeon(template: HitTriageTemplate,
117
131
  }
118
132
  const df = fileDf;
119
133
  df.name = fileDf.name;
120
- resolve({df, type: 'File', campaignProps: campaignPropsObject});
134
+ const name = campaignNameInput.value?.trim() || undefined;
135
+ const extraData = options?.getExtraData?.();
136
+ resolve({df, type: 'File', campaignProps: campaignPropsObject, friendlyName: name, extraData});
121
137
  } else {
122
138
  const func = dataSourceFunctionsMap[dataSourceFunctionInput.value!];
123
139
  if (!func) {
@@ -129,7 +145,9 @@ export async function newCampaignAccordeon(template: HitTriageTemplate,
129
145
  funcCallInputs[key] = value;
130
146
  });
131
147
  const df: DG.DataFrame = await func.apply(funcCallInputs);
132
- resolve({df, type: 'Query', campaignProps: campaignPropsObject});
148
+ const name = campaignNameInput.value?.trim() || undefined;
149
+ const extraData = options?.getExtraData?.();
150
+ resolve({df, type: 'Query', campaignProps: campaignPropsObject, friendlyName: name, extraData});
133
151
  };
134
152
  };
135
153
  const startCampaignButton = ui.bigButton(C.i18n.StartCampaign, () => onOkProxy());
@@ -0,0 +1,208 @@
1
+ /* eslint-disable max-len */
2
+ import * as DG from 'datagrok-api/dg';
3
+ import * as ui from 'datagrok-api/ui';
4
+ import * as grok from 'datagrok-api/grok';
5
+ import {_package} from '../../package';
6
+ import {CampaignFieldTypes, HitTriageCampaignField, HitTriageCampaignFieldType,
7
+ IComputeDialogResult, PepTriageTemplate, IngestType, INewTemplateResult} from '../types';
8
+ import * as C from '../consts';
9
+ import '../../../css/hit-triage.css';
10
+ import {chemFunctionsDialog} from '../dialogs/functions-dialog';
11
+ import {ItemType, ItemsGrid} from '@datagrok-libraries/utils/src/items-grid';
12
+ import {HitAppBase} from '../hit-app-base';
13
+ import {getLayoutInput} from './layout-input';
14
+ import {getFuncPackageNameSafe, loadTemplate} from '../utils';
15
+ import {getCampaignFieldEditors, saveTemplate} from './new-template-accordeon';
16
+
17
+
18
+ export async function createPepTriageTemplateAccordeon(app: HitAppBase<any>,
19
+ dataSourceFunctionMap: { [key: string]: DG.Func | DG.DataQuery | DG.Script },
20
+ preset?: PepTriageTemplate,
21
+ ): Promise<INewTemplateResult<PepTriageTemplate>> {
22
+ const templatesFolder = `${app.appName}/templates`;
23
+ const availableTemplates = (await _package.files.list(templatesFolder))
24
+ .filter((file) => file.name.endsWith('.json'))
25
+ .map((file) => file.name.slice(0, -5));
26
+ const availableTemplateKeys: string[] = [];
27
+ for (const tn of availableTemplates) {
28
+ const t: PepTriageTemplate = await loadTemplate<PepTriageTemplate>(`${templatesFolder}/${tn}.json`);
29
+ availableTemplateKeys.push(t.key);
30
+ }
31
+
32
+ const availableSubmitFunctions = Array.from(new Set(DG.Func.find({meta: {role: C.HitTriageSubmitTag}}).concat(DG.Func.find({tags: [C.HitTriageSubmitTag]}))));
33
+ const submitFunctionsMap: {[key: string]: DG.Func} = {};
34
+ availableSubmitFunctions.forEach((func) => {
35
+ submitFunctionsMap[func.friendlyName ?? func.name] = func;
36
+ });
37
+ const presetSubmitKey = preset?.submit ? Object.keys(submitFunctionsMap)
38
+ .find((k) => submitFunctionsMap[k]?.name === preset.submit?.fName) : null;
39
+ const submitFunctionInput =
40
+ ui.input.choice('Submit function', {value: presetSubmitKey ?? null, items: [null, ...Object.keys(submitFunctionsMap)]});
41
+ if (!presetSubmitKey)
42
+ submitFunctionInput.value = null;
43
+ submitFunctionInput.nullable = true;
44
+ submitFunctionInput.fireChanged();
45
+ submitFunctionInput.setTooltip('Select function to be called upon submitting');
46
+ const layoutInput = getLayoutInput();
47
+ const errorDiv = ui.divText('Template name is empty or already exists', {classes: 'hit-triage-error-div'});
48
+ const keyErrorDiv = ui.divText('Template key is empty or already exists', {classes: 'hit-triage-error-div'});
49
+
50
+ const templateNameInput = ui.input.string('Name', {value: preset ? `${preset.name} (copy)` : '', onValueChanged: (value) => {
51
+ if (value === '' || availableTemplates.includes(value)) {
52
+ templateNameInput.root.style.borderBottom = '1px solid red';
53
+ errorDiv.style.opacity = '100%';
54
+ } else {
55
+ templateNameInput.root.style.borderBottom = 'none';
56
+ errorDiv.style.opacity = '0%';
57
+ }
58
+ }});
59
+ const templateKeyInput = ui.input.string('Key', {value: preset ? `${preset.key}-copy` : '', onValueChanged: (value) => {
60
+ if (value === '' || availableTemplateKeys.includes(value)) {
61
+ templateKeyInput.root.style.borderBottom = '1px solid red';
62
+ keyErrorDiv.style.opacity = '100%';
63
+ } else {
64
+ templateKeyInput.root.style.borderBottom = 'none';
65
+ keyErrorDiv.style.opacity = '0%';
66
+ }
67
+ }});
68
+
69
+ templateKeyInput.setTooltip('Template key used for campaign prefix');
70
+ templateNameInput.setTooltip('Template name');
71
+ templateNameInput.root.style.borderBottom = 'none';
72
+ templateKeyInput.root.style.borderBottom = 'none';
73
+ errorDiv.style.opacity = '0%';
74
+ keyErrorDiv.style.opacity = '0%';
75
+
76
+ // PepTriage-specific inputs
77
+ const seqColInput = ui.input.string('Sequence Column', {value: preset?.sequenceColumnName ?? '', nullable: false});
78
+ seqColInput.setTooltip('Name of the sequence column in the dataset (mandatory)');
79
+ const seqColErrorDiv = ui.divText('Sequence column name is required', {classes: 'hit-triage-error-div'});
80
+ seqColErrorDiv.style.opacity = '0%';
81
+ seqColInput.onChanged.subscribe(() => {
82
+ if (!seqColInput.value?.trim()) {
83
+ seqColInput.root.style.borderBottom = '1px solid red';
84
+ seqColErrorDiv.style.opacity = '100%';
85
+ } else {
86
+ seqColInput.root.style.borderBottom = 'none';
87
+ seqColErrorDiv.style.opacity = '0%';
88
+ }
89
+ });
90
+
91
+ const molColInput = ui.input.string('Molecule Column', {value: preset?.moleculeColumnName ?? '', nullable: true});
92
+ molColInput.setTooltip('Name of the molecule column (optional). If empty, sequences will be auto-converted to molecules.');
93
+
94
+ let funcDialogRes: IComputeDialogResult | null = null;
95
+ const dummyTemplate = preset ? preset : {
96
+ compute: {
97
+ descriptors: {enabled: true, args: []},
98
+ functions: [],
99
+ },
100
+ } as unknown as PepTriageTemplate;
101
+ const funcInput = await chemFunctionsDialog(app, (res) => {funcDialogRes = res;}, () => null,
102
+ dummyTemplate, false);
103
+ funcInput.root.classList.add('hit-triage-new-template-functions-input');
104
+
105
+ if (Object.entries(dataSourceFunctionMap).length === 0) {
106
+ const dataSourceFunctions = Array.from(new Set(
107
+ DG.Func.find({meta: {role: C.PepTriageDataSourceTag}}).concat(DG.Func.find({tags: [C.PepTriageDataSourceTag]}))));
108
+ dataSourceFunctions.forEach((func) => {
109
+ dataSourceFunctionMap[func.friendlyName ?? func.name] = func;
110
+ });
111
+ }
112
+ const combinedSourceNames = Object.keys(dataSourceFunctionMap);
113
+ const dataSourceFunctionInput = ui.input.choice(
114
+ C.i18n.dataSourceFunction, {value: combinedSourceNames[0], items: combinedSourceNames});
115
+ const ingestTypeInput = ui.input.choice<IngestType>('Ingest using', {value: preset?.dataSourceType ?? 'Query', items: ['Query', 'File'],
116
+ onValueChanged: (value) => {
117
+ dataSourceFunctionInput.root.style.display = value === 'Query' ? 'block' : 'none';
118
+ }});
119
+ if (preset?.dataSourceType)
120
+ dataSourceFunctionInput.root.style.display = preset.dataSourceType === 'Query' ? 'block' : 'none';
121
+
122
+ const fieldsEditor = getCampaignFieldEditors(preset?.campaignFields);
123
+
124
+ const form = ui.div([
125
+ ui.h2('Details'),
126
+ ui.div([templateNameInput, errorDiv]),
127
+ ui.div([templateKeyInput, keyErrorDiv]),
128
+ ui.h2('Sequence Configuration'),
129
+ ui.div([seqColInput, seqColErrorDiv]),
130
+ molColInput.root,
131
+ ui.h2('Data Ingestion'),
132
+ ingestTypeInput.root,
133
+ dataSourceFunctionInput.root,
134
+ layoutInput.dataFileInput,
135
+ fieldsEditor.fieldsDiv,
136
+ ui.h2('Compute'),
137
+ funcInput.root,
138
+ ui.h2('Submit'),
139
+ submitFunctionInput.root,
140
+ ], 'ui-form');
141
+
142
+ const buttonsDiv = ui.buttonsInput([]);
143
+ const buttonsContainerDiv = buttonsDiv.getElementsByClassName('ui-input-editor')?.[0] ?? buttonsDiv;
144
+ form.appendChild(buttonsDiv);
145
+ const cancelPromise = new Promise<void>((resolve) => {
146
+ const cancelButton = ui.button(C.i18n.cancel, () => resolve());
147
+ buttonsContainerDiv.appendChild(cancelButton);
148
+ });
149
+
150
+ const promise = new Promise<PepTriageTemplate>((resolve) => {
151
+ async function onOkProxy() {
152
+ funcInput.okProxy();
153
+ if (errorDiv.style.opacity === '100%') {
154
+ grok.shell.error('Template name is empty or already exists');
155
+ return;
156
+ }
157
+ if (keyErrorDiv.style.opacity === '100%') {
158
+ grok.shell.error('Template key is empty or already exists');
159
+ return;
160
+ }
161
+ if (!seqColInput.value?.trim()) {
162
+ grok.shell.error('Sequence column name is required');
163
+ return;
164
+ }
165
+ const submitFunction = submitFunctionInput.value ? submitFunctionsMap[submitFunctionInput.value] : undefined;
166
+ const out: PepTriageTemplate = {
167
+ name: templateNameInput.value,
168
+ key: templateKeyInput.value,
169
+ sequenceColumnName: seqColInput.value!.trim(),
170
+ moleculeColumnName: molColInput.value?.trim() || undefined,
171
+ campaignFields: fieldsEditor.getFields(),
172
+ dataSourceType: ingestTypeInput.value ?? 'Query',
173
+ layoutViewState: layoutInput.getLayoutViewState() ?? undefined,
174
+ compute: {
175
+ descriptors: {
176
+ enabled: !!funcDialogRes?.descriptors?.length,
177
+ args: funcDialogRes?.descriptors ?? [],
178
+ },
179
+ functions: Object.entries(funcDialogRes?.externals ?? {}).map(([funcName, args]) => {
180
+ const splitFunc = funcName.split(':');
181
+ return ({name: splitFunc[1], package: splitFunc[0], args: args});
182
+ }),
183
+ scripts: Object.entries(funcDialogRes?.scripts ?? {})
184
+ .filter(([name, _]) => name.startsWith(C.HTScriptPrefix) && name.split(':').length === 3)
185
+ .map(([scriptId, args]) => {
186
+ const scriptNameParts = scriptId.split(':');
187
+ return ({name: scriptNameParts[1] ?? '', id: scriptNameParts[2] ?? '', args: args});
188
+ }),
189
+ queries: Object.entries(funcDialogRes?.queries ?? {})
190
+ .filter(([name, _]) => name.startsWith(C.HTQueryPrefix) && name.split(':').length === 3)
191
+ .map(([queryName, args]) => {
192
+ const queryNameParts = queryName.split(':');
193
+ return ({name: queryNameParts[1] ?? '', id: queryNameParts[2] ?? '', args: args});
194
+ }),
195
+ },
196
+ ...(submitFunction ? {submit: {fName: submitFunction.name, package: getFuncPackageNameSafe(submitFunction)}} : {}),
197
+ queryFunctionName: (ingestTypeInput.value === 'Query') ? dataSourceFunctionInput.value ?? undefined : undefined,
198
+ };
199
+ _package.files.writeAsText(`${templatesFolder}/${out.name}.json`, JSON.stringify(out));
200
+ grok.shell.info('Template created successfully');
201
+ resolve(out);
202
+ }
203
+ const createTemplateButton = ui.bigButton(C.i18n.createTemplate, () => onOkProxy());
204
+ buttonsContainerDiv.appendChild(createTemplateButton);
205
+ });
206
+
207
+ return {root: form, template: promise, cancelPromise};
208
+ }
@@ -11,16 +11,17 @@ import {chemFunctionsDialog} from '../dialogs/functions-dialog';
11
11
  import {ItemType, ItemsGrid} from '@datagrok-libraries/utils/src/items-grid';
12
12
  import {HitAppBase} from '../hit-app-base';
13
13
  import {getLayoutInput} from './layout-input';
14
- import {getFuncPackageNameSafe} from '../utils';
14
+ import {getFuncPackageNameSafe, loadTemplate} from '../utils';
15
15
 
16
16
 
17
17
  export async function createTemplateAccordeon(app: HitAppBase<any>,
18
18
  dataSourceFunctionMap: { [key: string]: DG.Func | DG.DataQuery | DG.Script },
19
+ preset?: HitTriageTemplate,
19
20
  ): Promise<INewTemplateResult<HitTriageTemplate>> {
20
21
  const availableTemplates = (await _package.files.list('Hit Triage/templates')).map((file) => file.name.slice(0, -5));
21
22
  const availableTemplateKeys: string[] = [];
22
23
  for (const tn of availableTemplates) {
23
- const t: HitTriageTemplate = JSON.parse(await _package.files.readAsText(`Hit Triage/templates/${tn}.json`));
24
+ const t: HitTriageTemplate = await loadTemplate<HitTriageTemplate>(`Hit Triage/templates/${tn}.json`);
24
25
  availableTemplateKeys.push(t.key);
25
26
  }
26
27
 
@@ -29,9 +30,12 @@ export async function createTemplateAccordeon(app: HitAppBase<any>,
29
30
  availableSubmitFunctions.forEach((func) => {
30
31
  submitFunctionsMap[func.friendlyName ?? func.name] = func;
31
32
  });
33
+ const presetSubmitKey = preset?.submit ? Object.keys(submitFunctionsMap)
34
+ .find((k) => submitFunctionsMap[k]?.name === preset.submit?.fName) : null;
32
35
  const submitFunctionInput =
33
- ui.input.choice('Submit function', {value: null, items: [null, ...Object.keys(submitFunctionsMap)]});
34
- submitFunctionInput.value = null;
36
+ ui.input.choice('Submit function', {value: presetSubmitKey ?? null, items: [null, ...Object.keys(submitFunctionsMap)]});
37
+ if (!presetSubmitKey)
38
+ submitFunctionInput.value = null;
35
39
  submitFunctionInput.nullable = true;
36
40
  submitFunctionInput.fireChanged();
37
41
  submitFunctionInput.setTooltip('Select function to be called upon submitting');
@@ -40,7 +44,7 @@ export async function createTemplateAccordeon(app: HitAppBase<any>,
40
44
 
41
45
  const keyErrorDiv = ui.divText('Template key is empty or already exists', {classes: 'hit-triage-error-div'});
42
46
 
43
- const templateNameInput = ui.input.string('Name', {value: '', onValueChanged: (value) => {
47
+ const templateNameInput = ui.input.string('Name', {value: preset ? `${preset.name} (copy)` : '', onValueChanged: (value) => {
44
48
  if (value === '' || availableTemplates.includes(value)) {
45
49
  templateNameInput.root.style.borderBottom = '1px solid red';
46
50
  errorDiv.style.opacity = '100%';
@@ -49,7 +53,7 @@ export async function createTemplateAccordeon(app: HitAppBase<any>,
49
53
  errorDiv.style.opacity = '0%';
50
54
  }
51
55
  }});
52
- const templateKeyInput = ui.input.string('Key', {value: '', onValueChanged: (value) => {
56
+ const templateKeyInput = ui.input.string('Key', {value: preset ? `${preset.key}-copy` : '', onValueChanged: (value) => {
53
57
  if (value === '' || availableTemplateKeys.includes(value)) {
54
58
  templateKeyInput.root.style.borderBottom = '1px solid red';
55
59
  keyErrorDiv.style.opacity = '100%';
@@ -68,7 +72,7 @@ export async function createTemplateAccordeon(app: HitAppBase<any>,
68
72
 
69
73
  let funcDialogRes: IComputeDialogResult | null = null;
70
74
  // used just for functions editor
71
- const dummyTemplate = {
75
+ const dummyTemplate = preset ? preset : {
72
76
  compute: {
73
77
  descriptors: {
74
78
  enabled: true,
@@ -90,12 +94,14 @@ export async function createTemplateAccordeon(app: HitAppBase<any>,
90
94
  const combinedSourceNames = Object.keys(dataSourceFunctionMap);
91
95
  const dataSourceFunctionInput = ui.input.choice(
92
96
  C.i18n.dataSourceFunction, {value: combinedSourceNames[0], items: combinedSourceNames});
93
- const ingestTypeInput = ui.input.choice<IngestType>('Ingest using', {value: 'Query', items: ['Query', 'File'],
97
+ const ingestTypeInput = ui.input.choice<IngestType>('Ingest using', {value: preset?.dataSourceType ?? 'Query', items: ['Query', 'File'],
94
98
  onValueChanged: (value) => {
95
99
  dataSourceFunctionInput.root.style.display = value === 'Query' ? 'block' : 'none';
96
100
  }});
101
+ if (preset?.dataSourceType)
102
+ dataSourceFunctionInput.root.style.display = preset.dataSourceType === 'Query' ? 'block' : 'none';
97
103
 
98
- const fieldsEditor = getCampaignFieldEditors();
104
+ const fieldsEditor = getCampaignFieldEditors(preset?.campaignFields);
99
105
 
100
106
  const form = ui.div([
101
107
  ui.h2('Details'),
package/src/app/consts.ts CHANGED
@@ -1,4 +1,7 @@
1
- import type {HitDesignCampaign} from './types';
1
+ import type {HitDesignCampaign, HitTriageCampaign} from './types';
2
+
3
+ // Re-export shared compute-function constants from the statistics library
4
+ export {funcTypeNames, HTScriptPrefix, HTQueryPrefix, ComputeQueryMolColName} from '@datagrok-libraries/statistics/src/compute-functions/consts';
2
5
 
3
6
  export const CampaignIdKey = 'campaignId';
4
7
  export const HitDesignCampaignIdKey = 'campaignId';
@@ -16,9 +19,6 @@ export const ViDColName = 'V-iD';
16
19
  export const ViDSemType = 'HIT_DESIGN_VID';
17
20
  export const HTcampaignName = 'HTcampaignName';
18
21
  export const HDcampaignName = 'HDcampaignName';
19
- export const HTScriptPrefix = 'HTScript';
20
- export const HTQueryPrefix = 'HTQuery';
21
- export const ComputeQueryMolColName = 'molecules';
22
22
  export const i18n = {
23
23
  startNewCampaign: 'New Campaign',
24
24
  createNewCampaign: 'New Campaign',
@@ -35,12 +35,6 @@ export const i18n = {
35
35
  noInformation: 'No Information',
36
36
  } as const;
37
37
 
38
- export const funcTypeNames = {
39
- script: 'script',
40
- function: 'function-package',
41
- query: 'data-query',
42
- } as const;
43
-
44
38
  export const HDCampaignsGroupingLSKey = 'HDCampaignsGrouping';
45
39
  export const HDCampaignTableColumnsLSKey = 'HDCampaignTableColumns';
46
40
  export const HDCampaignsTableSortingLSKey = 'HDCampaignsTableSorting';
@@ -67,4 +61,23 @@ export const DefaultCampaignTableInfoGetters = {
67
61
 
68
62
  export type CampaignTableColumns = keyof typeof DefaultCampaignTableInfoGetters | `campaignFields.${string}`;
69
63
 
64
+ export const HTCampaignsGroupingLSKey = 'HTCampaignsGrouping';
65
+ export const HTCampaignTableColumnsLSKey = 'HTCampaignTableColumns';
66
+ export const HTCampaignsTableSortingLSKey = 'HTCampaignsTableSorting';
67
+
68
+ export const HTDefaultCampaignTableInfoGetters = {
69
+ 'Name': (info: HitTriageCampaign) => info.friendlyName ?? info.name,
70
+ 'Code': (info: HitTriageCampaign) => info.name,
71
+ 'Created': (info: HitTriageCampaign) => info.createDate,
72
+ 'Author': (info: HitTriageCampaign) => info.authorUserFriendlyName ?? '',
73
+ 'Last Modified by': (info: HitTriageCampaign) => info.lastModifiedUserName ?? '',
74
+ 'Total': (info: HitTriageCampaign) => (info.rowCount ?? 0).toString(),
75
+ 'Selected': (info: HitTriageCampaign) => (info.filteredRowCount ?? 0).toString(),
76
+ 'Status': (info: HitTriageCampaign) => info.status,
77
+ } as const;
78
+
79
+ export type HTCampaignTableColumns = keyof typeof HTDefaultCampaignTableInfoGetters | `campaignFields.${string}`;
80
+
81
+ export const PepTriageDataSourceTag = 'pepTriageDataSource';
82
+
70
83
  export const HTFunctionOrderingLSKey = 'HTFunctionOrderingLS';